正在加载……
专注、离线、切勿分心
InetAddress.h InetAddress.cpp
#ifndef __INETADDRESS_H__
#define __INETADDRESS_H__
#include<iostream>
#include<netinet/in.h>
using namespace std;
namespace meihao
{
        class InetAddress
        {
                public:
                        InetAddress(unsigned short port);
                        InetAddress(const string& ip,unsigned short port);
                        InetAddress(struct sockaddr_in addr);
                        const struct sockaddr_in* getInetAddressPtr();
                        string ip()const;
                        unsigned short port()const;
                private:
                        struct sockaddr_in _addr;
        };
};
#endif
#include"InetAddress.h"
#include<iostream>
#include<strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
namespace meihao
{
        InetAddress::InetAddress(unsigned short port)
        {
                bzero(&_addr,sizeof(_addr));
                _addr.sin_family = AF_INET;
                _addr.sin_port = htons(port);
                _addr.sin_addr.s_addr = INADDR_ANY;
        }
        InetAddress::InetAddress(const string& ip,unsigned short port)
        {
                bzero(&_addr,sizeof(_addr));
                _addr.sin_family = AF_INET;
                _addr.sin_port = htons(port);
                _addr.sin_addr.s_addr = inet_addr(ip.c_str());
        }
        InetAddress::InetAddress(struct sockaddr_in addr)
        {
                _addr = addr;
        }
        const struct sockaddr_in* InetAddress::getInetAddressPtr()
        {
                return &_addr;
        }
        string InetAddress::ip()const
        {
                return string( inet_ntoa(_addr.sin_addr) );
        }
        unsigned short InetAddress::port()const
        {
                return ntohs(_addr.sin_port);
        }
};
Socket.h Socket.cpp
#ifndef __SOCKET_H__
#define __SOCKET_H__
#include<iostream>
#include"InetAddress.h"
using namespace std;
namespace meihao
{
        class Socket
        {
                public:
                        Socket();
                        Socket(int fd);
                        void ready(const InetAddress& addr);
                        int accept();
                        void shutdownWrite();
                        int fd();
                        static InetAddress getLocalAddress(int fd);
                        static InetAddress getPeerAddress(int fd);
                private:
                        void setReuseAddr(bool on);
                        void setReusePort(bool on);
                        void bind(const InetAddress& addr);
                        void listen();
                private:
                        int _fd;
        };
};
#endif
#include"Socket.h"
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
using namespace std;
#define handle_error(msg) \
        do{\
                perror(msg);\
        exit(-1);\
        }while(0);
namespace meihao
{
        int getSocketfd()
        {
                int sfd = socket(AF_INET,SOCK_STREAM,0);
                if(-1==sfd)
                {
                        handle_error("socket");
                }
        }
        Socket::Socket():_fd(getSocketfd())
        {
        }
        Socket::Socket(int fd):_fd(fd)
        {
        }
        void Socket::setReuseAddr(bool on)
        {
                int val = on?1:0;
                int ret = setsockopt(_fd,SOL_SOCKET,SO_REUSEADDR,(const void*)&val,sizeof(int));
                if(-1==ret)
                {
                        handle_error("setsockopt");
                }
        }
        void Socket::setReusePort(bool on)
        {
                int val = on?1:0;
                int ret = setsockopt(_fd,SOL_SOCKET,SO_REUSEPORT,(const void *)&val,sizeof(int));
                if(-1==ret)
                {
                        handle_error("setsockopt");
                }
        }
        void Socket::bind(const InetAddress& addr)
        {
                int ret = ::bind(_fd,(const struct sockaddr*)&addr,(socklen_t)sizeof(addr));
                if(-1==ret)
                {
                        handle_error("::bind");
                }
        }
        void Socket::listen()
        {
                int ret = ::listen(_fd,10);
                if(-1==ret)
                {
                        handle_error("::listen");
                }
        }
        void Socket::ready(const InetAddress& addr)
        {
                setReuseAddr(true);
                setReusePort(true);
                bind(addr);
                listen();
        }
        int Socket::accept()
        {
                int new_fd = ::accept(_fd,NULL,NULL);
                if(-1==new_fd)
                {
                        handle_error("::accept");
                }
        }
        void Socket::shutdownWrite()
        {
                int ret = ::shutdown(_fd,SHUT_WR);
                if(-1==ret)
                {
                        handle_error("::shutdown");
                }
        }
        int Socket::fd()
        {
                return _fd;
        }
        InetAddress Socket::getLocalAddress(int fd)
        {
                struct sockaddr_in addr;
                bzero(&addr,sizeof(addr));
                int addrlen = sizeof(addr);
                int ret = ::getsockname(fd,(struct sockaddr*)&addr,(socklen_t*)&addrlen);
                if(-1==ret)
                {
                        handle_error("::getsockname");
                }
                return InetAddress(addr);
        }
        InetAddress Socket::getPeerAddress(int fd)
        {
                struct sockaddr_in addr;
                bzero(&addr,sizeof(addr));
                int addrlen = sizeof(addr);
                int ret = ::getpeername(fd,(struct sockaddr*)&addr,(socklen_t*)&addrlen);
                if(-1==ret)
                {
                        handle_error("::getpeername");
                }
                return InetAddress(addr);
        }
};
SocketIO.h SocketIO.cpp
#ifndef __SOCKETIO_H__
#define __SOCKETIO_H__
#include<iostream>
using namespace std;
namespace meihao
{
        class SocketIO
        {
                public:
                        SocketIO(int fd);
                        int readn(char* buf,int count);
                        int writen(const char* buf,int count);
                        int readline(char* buf,int maxlen);  // 成功返回读取字节数,失败返回-1
                private:
                        int recvPeek(char* buf,int count);  // 预读取一行
                private:
                        int _fd;
        };
};
#endif

#include"SocketIO.h"
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
namespace meihao
{
        SocketIO::SocketIO(int fd):_fd(fd)
        {

        }
        int SocketIO::readn(char* buf,int count)
        {
                int left = count;
                char* ptmp = buf;
                while(left>0)
                {
                        int nread = ::recv(_fd,ptmp,left,0);
                        if(-1==nread)
                        {
                                if(errno==EINTR)
                                        continue;
                                exit(EXIT_FAILURE);
                        }
                        else if(0==nread)
                                break;
                        left -= nread;
                        ptmp += nread;
                }
                return count-left;  // 返回发送的数据个数
        }
        int  SocketIO::writen(const char* buf,int count)
        {
                int left = count;
                const char* ptmp = buf;
                while(left>0)
                {
                        int nread = ::send(_fd,ptmp,left,0);
                        if(-1==nread)
                        {
                                if(errno==EINTR)
                                        continue;
                                exit(EXIT_FAILURE);
                        }
                        else if(0==nread)
                                break;
                        left -= nread;
                        ptmp += nread;
                }
                return count-left;
        }
        int SocketIO::recvPeek(char* buf,int count)
        {
                int nread = ::recv(_fd,buf,count,MSG_PEEK);
                if(-1==nread)
                {
                        perror("recvPeek");
                }
        }
        int SocketIO::readline(char* buf,int maxlen)  // 失败返回-1
        {
                int left = maxlen;
                char* ptmp = buf;
                int nread = 0;
                while(left>0)
                {
                        nread = recvPeek(ptmp,left);
                        if(-1==nread)
                        {
                                if(errno==EINTR)
                                        continue;
                                return -1;
                        }
                        else if(0==nread)
                                return -1;
                        for(int idx=0;idx<nread;idx++)
                        {
                                if(ptmp[idx]=='\n')
                                {
                                        if(readn(ptmp,nread)!=nread)
                                                return -1;
                                        left -= nread;
                                        return maxlen-left;
                                }
                        }
                        // 预读取一行没有读到换行
                        if(readn(ptmp,nread)!=nread)
                                return -1;
                        ptmp += nread;
                        left -= nread;
                }
                return maxlen-left;
        }
};
TcpConnection.h TcpConnection.cpp
#ifndef __TCPCONNECTION_H__
#define __TCPCONNECTION_H__
#include<iostream>
#include"SocketIO.h"
#include"Socket.h"
#include"InetAddress.h"
#include<memory>
using namespace std;
namespace meihao
{
        class TcpConnection;
        typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
        //利用智能共享指针托管TcpConnection类型指针,这样不用考虑最后的资源释放
        typedef function<void(const TcpConnectionPtr& conn)> TcpConnectionCallback;   // 这里要为public,不然后面的Epoll类访问不到,最开始我放到类里面了,放到外面访问更方便
        //function函数对象绑定回调函数
        class TcpConnection:public std::enable_shared_from_this<TcpConnection>
        {
                //继承enable_shared_from_this,使用里面的shared_from_this()方法传递指向函数
                //本身的指针,防止出现shared_ptr误用造成多次释放或者拷贝
                public:
                        TcpConnection(int confd);  // 连接到的fd
                        ~TcpConnection();
                        string receive();
                        void send(const string& msg);
                        void shutdown();
                        string toString();  // 输出服务器端信息和连接上的客户端的信息

                        void setConnectionCallback(TcpConnectionCallback cb);
                        void setMessageCallback(TcpConnectionCallback cb);
                        void setCloseCallback(TcpConnectionCallback cb);

                        void handleConnectionCallback();  // 客户端连接上后做出的操作
                        void handleMessageCallback();  // 服务器端-客户端之间发送消息
                        void handleCloseCallback();  // 服务器端关闭连接做出的行为
                private:
                        Socket _sock;
                        SocketIO _sockIO;
                        InetAddress _localAddress;
                        InetAddress _peerAddress;
                        bool _isShutdownWrite;
                        TcpConnectionCallback _onConnectionCb;  // 请求连接到服务器做出的行为
                        TcpConnectionCallback _onMessageCb;  // 双方发送消息
                        TcpConnectionCallback _onCloseCb;  // 关闭链接行为
        };
};
#endif
#include"TcpConnection.h"
#include<iostream>
#include<strings.h>
#include<stdlib.h>
#include<sstream>
#include<errno.h>
#define handle_error(msg) \
        do{\
                perror(msg);\
                exit(-1);\
        }while(0);
using namespace std;
namespace meihao
{
        TcpConnection::TcpConnection(int confd):_sock(confd)
                                                                                        ,_sockIO(confd)
                                                                                        ,_localAddress(meihao::Socket::getLocalAddress(confd))
                                                                                        ,_peerAddress(meihao::Socket::getPeerAddress(confd))
                                                                                    ,_isShutdownWrite(false)
        {
                //剩下的回调函数使用成员函数来设置,方便使用过程中修改
        }
        TcpConnection::~TcpConnection()
        {
        }
        string TcpConnection::receive()
        {
                char buf[1024];
                bzero(buf,sizeof(buf));
                int ret = _sockIO.readline(buf,sizeof(buf));
                if(-1==ret)
                {
                        handle_error("recvive");
                }
                return string(buf);
        }
        void TcpConnection::send(const string& msg)
        {
                        _sockIO.writen(msg.c_str(),msg.size());
        }
        void TcpConnection::shutdown()
        {
                if(!_isShutdownWrite)  // 没有关闭
                {
                        _isShutdownWrite = true;
                        _sock.shutdownWrite();
                }
        }
        string TcpConnection::toString()
        {
                ostringstream oss;
                oss<<_localAddress.ip()<<" "<<_localAddress.port()<<"--->"
                        <<_peerAddress.ip()<<" "<<_peerAddress.port()<<endl;
                return oss.str();
        }

        // 开始初始化类里面的回调函数的值
        void TcpConnection::setConnectionCallback(TcpConnectionCallback cb)
        {
                _onConnectionCb = cb;
        }
        void TcpConnection::setMessageCallback(TcpConnectionCallback cb)
        {
                _onMessageCb = cb;
        }
        void TcpConnection::setCloseCallback(TcpConnectionCallback cb)
        {
                _onCloseCb = cb;
        }

        void TcpConnection::handleConnectionCallback()
        {
                if(_onConnectionCb)
                {
                        _onConnectionCb(shared_from_this());  // 传调用该函数的指针本身
                        // 等价于 shared_ptr<TcpConnection>(this);
                }
        }
        void TcpConnection::handleMessageCallback()
        {
                if(_onMessageCb)
                {
                        _onMessageCb(shared_from_this());
                }
        }
        void TcpConnection::handleCloseCallback()
        {
                if(_onCloseCb)
                {
                        _onCloseCb(shared_from_this());
                }
        }
};
Epoll.h Epoll.cpp
#ifndef __EPOLL_H__
#define __EPOLL_H__
#include<iostream>
#include"TcpConnection.h"
#include<vector>
#include<sys/epoll.h>
#include<map>
using namespace std;
namespace meihao
{
        class Epoll
        {
                public:
                        Epoll(int sfd);
                        void loop();
                        void unloop();
                        void waitEpollfd();
                        //在epoll类中用函数设置TcpConnection类中的私有变量
                        void epollSetConnectionCallback(TcpConnectionCallback cb);
                        void epollSetMessageCallback(TcpConnectionCallback cb);
                        void epollSetCloseCallback(TcpConnectionCallback cb);
                private:
                        void handleConnection();  //epoll处理新连接的客户端
                        void handleMessage(int connfd);  //处理消息
                        bool isConnected(int connfd);  //查看描述符是否断开
                private:
                        int _efd;  // epoll_create得到的fd
                        int _sfd;  // sockfd,用来监听客户端请求
                        bool _isLooping;  // 是否还在监听客户端请求
                        vector<struct epoll_event> _events;  //存放epoll_wait返回的监听描述符事件数组
                        map<int,TcpConnectionPtr> _mapConnections;  // 存放所有客户端请求的描述符和对应的智能指针

                        TcpConnectionCallback _onConnectionCb;  // 为监听到的客户端设置连接的回调函数
                        TcpConnectionCallback _onMessageCb;
                        TcpConnectionCallback _onCloseCb;
        };
};
#endif

#include"Epoll.h"
#include<iostream>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/epoll.h>
using namespace std;
#define handle_error(msg)\
        do{\
                perror(msg);\
                exit(EXIT_FAILURE);\
        }while(0);
namespace meihao
{
        int createEpollfd()
        {
                int efd = ::epoll_create(1);  //参数不为0即可
                if(-1==efd)
                {
                        handle_error("epoll_create");
                }
                return efd;
        }
        void addEpollfd(int efd,int fd)
        {
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = fd;
                int ret = epoll_ctl(efd,EPOLL_CTL_ADD,fd,&event);
                if(-1==ret)
                {
                        handle_error("epoll_ctl");
                }
        }
        void delEpollfd(int efd,int fd)
        {
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = fd;
                int ret = epoll_ctl(efd,EPOLL_CTL_DEL,fd,&event);
                if(-1==ret)
                {
                        handle_error("epoll_ctl_del");
                }
        }
        Epoll::Epoll(int sfd):_efd(createEpollfd())
                                                  ,_sfd(sfd)
                                                                        ,_isLooping(false)
                                                                                                  ,_events(1024)  // 容器提前开辟空间
        {
                addEpollfd(_efd,_sfd);
        }
        void Epoll::loop()  // 开启监听客户端连接请求
        {
                _isLooping = true;
                while(_isLooping)
                {
                        waitEpollfd();
                }
        }
        void Epoll::unloop() //关闭epoll监听
        {
                if(_isLooping)
                        _isLooping = false;
        }
        void Epoll::waitEpollfd()
        {
                int ret;
                do
                {
                        ret = ::epoll_wait(_efd,&(*_events.begin()),_events.size(),5000);
                        // 事件满足的fd会放到vector里面
                }while(-1==ret&&errno==EINTR);  // 被中断打断
                if(-1==ret)
                {
                        handle_error("epoll_wait");
                }
                else if(0==ret)  // 5S后超时返回
                {
                        cout<<"Epoll wait timeout!"<<endl;
                }
                for(int idx=0;idx!=ret;++idx)
                {
                        if(_events[idx].data.fd ==_sfd &&_events[idx].events == EPOLLIN)
                        {//处理新连接
                                handleConnection();
                        }
                        else if(_events[idx].events == EPOLLIN) 
                        {//处理已经连接上的客户端的输入请求
                                handleMessage(_events[idx].data.fd);
                        }
                }
        }
        void Epoll::handleConnection()
        {
                int connfd = ::accept(_sfd,NULL,NULL);
                addEpollfd(_efd,connfd); 
                //处理新的TCP链接
                TcpConnectionPtr con(new TcpConnection(connfd));
                cout<<"TcpConnectionPtr"<<con<<endl;
                //设置TcpConnectionPtr的回调函数
                con->setConnectionCallback(_onConnectionCb);
                con->setMessageCallback(_onMessageCb);
                con->setCloseCallback(_onCloseCb);
                _mapConnections.insert(::make_pair(connfd,con));  //map中插入新连接的请求
                con->handleConnectionCallback();
        }
        void Epoll::handleMessage(int connfd)
        {
                auto it = _mapConnections.find(connfd);  //找到map中存放的对应描述符的关键字
                if(it!=_mapConnections.end())  //如果描述符确实存在
                {
                        bool flag = isConnected(connfd);
                        if(flag)
                        {//链接没有断开
                                it->second->handleMessageCallback();  //tcp连接处理消息,调用的是TcpConnection类里的函数
                        }
                        else
                        {//断开
                                delEpollfd(_efd,connfd);
                                it->second->handleCloseCallback();  //打印显示断开信息
                                _mapConnections.erase(it);  //map中删除对应的pair
                        }
                }
        }
        bool Epoll::isConnected(int connfd)
        {
                int ret;
                char buf[512];
                do
                {
                        ret = recv(connfd,buf,sizeof(buf),MSG_PEEK);  //从内核缓冲区预读取
                }while(-1==ret&&errno==EINTR);
                return ret>0;  //>0表示有数据可读,链接没有断开
        }
        void Epoll::epollSetConnectionCallback(TcpConnectionCallback cb)
        {
                _onConnectionCb = cb;
        }
        void Epoll::epollSetMessageCallback(TcpConnectionCallback cb)
        {
                _onMessageCb = cb;
        }
        void Epoll::epollSetCloseCallback(TcpConnectionCallback cb)
        {
                _onCloseCb = cb;
        }
};
TcpServer.h TcpServer.cpp
#ifndef __TCPSERVER_H__
#define __TCPSERVER_H__
#include<iostream>
#include"InetAddress.h"
#include"Socket.h"
#include"Epoll.h"
#include"TcpConnection.h"
using namespace std;
namespace meihao
{
        class TcpServer
        {
                public:
                        TcpServer(unsigned short port);
                        TcpServer(const string& ip,unsigned short port);
                        TcpServer(const InetAddress& addr);
                        void start();
                        void stop();
                        void tcpServerSetConnectionCallback(TcpConnectionCallback cb);
                        void tcpServerSetMessageCallback(TcpConnectionCallback cb);
                        void tcpServerSetCloseCallback(TcpConnectionCallback cb);
                private:
                        InetAddress _addr;
                        Socket _serverSock;
                        Epoll _epoll;
                        TcpConnectionCallback _onConnectionCb;
                        TcpConnectionCallback _onMessageCb;
                        TcpConnectionCallback _onCloseCb;
        };
};
#endif
#include"TcpServer.h"
#include<iostream>
using namespace std;
namespace meihao
{
        TcpServer::TcpServer(unsigned short port):_addr(port)
                                                                                        ,_serverSock()
                                                                                    ,_epoll(_serverSock.fd())
        {
        }
        TcpServer::TcpServer(const string& ip,unsigned short port):_addr(ip,port)
                                                                                                                        ,_serverSock()
                                                                                                                        ,_epoll(_serverSock.fd())
        {
        }
        TcpServer::TcpServer(const InetAddress& addr):_addr(addr)
                                                                                                ,_serverSock()
                                                                                            ,_epoll(_serverSock.fd())
        {
        }
        void TcpServer::start()
        {
                _serverSock.ready(_addr);  // 开启服务器,并监听
                //epoll类设置请求的tcp连接的回调函数
                _epoll.epollSetConnectionCallback(_onConnectionCb);
                _epoll.epollSetMessageCallback(_onMessageCb);
                _epoll.epollSetCloseCallback(_onCloseCb);
                _epoll.loop();  // epoll_wait客户端连接请求
        }
        void TcpServer::stop()
        {
                _epoll.unloop();
                _serverSock.shutdownWrite();
        }
        void TcpServer::tcpServerSetConnectionCallback(TcpConnectionCallback cb)
        {
                _onConnectionCb = cb;
        }
        void TcpServer::tcpServerSetMessageCallback(TcpConnectionCallback cb)
        {
                _onMessageCb = cb;
        }
        void TcpServer::tcpServerSetCloseCallback(TcpConnectionCallback cb)
        {
                _onCloseCb = cb;
        }
};
server.cpp Makefile
#include<iostream>
#include"TcpServer.h"
#include"TcpConnection.h"
using namespace std;
void onConnection(const meihao::TcpConnectionPtr& conn)
{
        cout<<conn->toString()<<"has connected!"<<endl;
        conn->send("connect success!\n");
}
void onMessage(const meihao::TcpConnectionPtr& conn)
{
        string msg = conn->receive();
        cout<<"recv:"<<msg;
        conn->send(msg);
}
void onClosed(const meihao::TcpConnectionPtr& conn)
{
        cout<<conn->toString()<<"has closed!"<<endl;
}
int main()
{
        meihao::TcpServer tcpServer(8848);
        tcpServer.tcpServerSetConnectionCallback(onConnection);
        tcpServer.tcpServerSetMessageCallback(onMessage);
        tcpServer.tcpServerSetCloseCallback(onClosed);
        tcpServer.start();
}
OBJS:=*.cpp
ELF:=server
$(ELF):$(OBJS)
        g++ $(OBJS) -o $(ELF) -std=c++11
.PHONY:rebuild clean
rebuild:
        make clean $(ELF)
clean:
        rm -rf $(ELF)


client.cpp
#include<iostream>
#include"InetAddress.h"
#include"SocketIO.h"
#include<strings.h>
#include<unistd.h>
using namespace std;
int main()
{
        meihao::InetAddress inetAddr(8848);
        int sfd = socket(AF_INET,SOCK_STREAM,0);
        if(-1==sfd)
        {
                perror("socket");
                exit(EXIT_FAILURE);
        }
        int ret = connect(sfd,(const struct sockaddr*)inetAddr.getInetAddressPtr(),sizeof(struct sockaddr));
        if(-1==ret)
        {
                perror("connect");
                exit(EXIT_FAILURE);
        }
        meihao::SocketIO socketIO(sfd);
        char buf[1024] = "";
        socketIO.readline(buf,sizeof(buf));
        cout<<buf;
        while(1)
        {
                bzero(buf,sizeof(buf));
                read(0,buf,sizeof(buf));
                socketIO.writen(buf,sizeof(buf));
                bzero(buf,sizeof(buf));
                socketIO.readline(buf,sizeof(buf));
                cout<<"recv from server:"<<buf;
        }
}

               



posted on 2018-07-25 21:21  正在加载……  阅读(465)  评论(0编辑  收藏  举报