socket网络服务实战

一、epoll模型的构建

由于网络服务高并发的需求,一般socket网络模型都采用epoll模型,有关epoll模型的原理在相关论坛中有许多讲述,在此不做重复讲解,主要讲一讲epoll模型的封装实现。

EPoller类的具体实现如下代码所示:

class EPoller
{
public:

    int Create(int maxfd);
    void JoinSocket(Socket* sock, unsigned int flag);
    void DelSocket(CEpollSocket* sock);
    int  LoopForEvent(int timeout);
    int  EpollWait(int timeout);
protected:
    int epoll_fd;    //epoll文件描述符
    epoll_event* p_events;  //用于保存epoll_wait返回的事件
    int max_fd;      //最大监听的文件描述符数量

};

// 创建epoll模型
int EPoller::Create(int maxfd)
{
    max_fd = maxfd ;
    epoll_fd = epoll_create(maxfd);

    p_events = new epoll_event[maxfd];
    memset(p_events, 0, sizeof(epoll_event) * maxfd);
    return 0;
}

//将某个fd添加至监听的队列中,并利用flag设置监听的事件类型
//其中Socket为基类
int EPoller::JoinSocket(Socket* sock, unsigned int flag)
{
    int fd = sock->GetSockHandle();
    epoll_event event;
    event.data.ptr = sock;
    event.events = flag|EPOLLHUP|EPOLLERR;

    if (epoll_ctl(_epoll_fd, EPOLL_CTL_MOD , fd, &ev) < 0)
    {
        if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD , fd, &ev) < 0)
        {
            return -1;
        }
    }

    return 0;
}

//删除指定的fd
int EPoller::DelSocket(Socket* sock)
{
    int fd = sock->GetSockHandle();//利用Socket对象取得相应的文件句柄
    if ( fd > 0 )
    {
        epoll_event ev;
        ev.data.ptr = sock;
        ev.events = 0;
        if (epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, &ev) < 0)
        {
            return -1;
        }

        return 0;
    }

    return -1;
}

//等待监控的事件触发
int EPoller::EpollWait(int timeout)
{
    int fd;
    int nfds;
    Socket*  sock;
    unsigned int flag;

    nfds = epoll_wait(epoll_fd, p_events, max_fd, timeout);
    if (nfds < 0)
    {
        return -1;
    }
    else if ( nfds == 0 )
    {
        return 0 ;
    }

    for (int i=0; i<nfds; i++)
    {
        sock = (Socket*)((p_events+i)->data.ptr);
        if ( sock == NULL )
        {
            continue;
        }
        flag = (p_events+i)->events;
        if (flag & EPOLLIN)
        {
            sock->Recv();
        }
        else if (flag & EPOLLOUT)
        {
            sock->Send();
        }
        else if (flag & EPOLLHUP)
        {
            sock->Close();
        }
        else if (flag & EPOLLERR)
        {
            sock->Error();
        }
    }

    return nfds ;
}
// 循环等待事件的触发
int EPoller::LoopForEvent(int timeout)
{
    while(true)
    {
        int result = EpollWait(timeout);
        if( result <= 0 )
        {
            return result;
        }
    }
}

其中的Socket类型作为其他网络通信服务类的基类,主要为其他的socket网络通信类提供一个统一的入口,其结构定义如下:

class Socket
{
    friend class EPoller;
public:
    int GetSockHandle();
    virtual int SendMsg(char* buffer, int length);
    
protected:
    virtual int Recv();
    virtual int Send();
    virtual int Close();
    virtual int Error();
    
    EPoller* epoller;
    int fd;
    
}

int Socket::SendMsg(char* buff, int len)
{
  int ret;
  int send_len = 0;
  int _len = len;
  errno = 0;
  while(_len > 0)
  {
    ret = send(fd, (void*)(buff+send_len), _len, 0);
    if (ret <= 0)
    {
      if (errno == EINTR || errno == EAGAIN)
      {
        usleep(10);
        continue;
      }
      break;
    }
    send_len += ret;
    _len -= ret;
  }
  return send_len;
}

 

二、UDP网络通信的实现

//Udp创建socket的流程
int CreateUdpSocket()
{
    // 1.创建socket
    sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    // 2.设置非阻塞
    fcntl(sock_fd, F_GETFL, 0);
    fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);
    
    // 3.绑定端口与ip
    struct sockaddr_in addr;
    string ip = "0.0.0.0";
    addr.sin_family = AF_INET;
    addr.sin_port = htons(5678);
    ip2uint(ip.c_str(), &(addr.sin_addr));
    bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
    
    // 4.设置socket缓存大小
    int recv_buf_size = 1024;
    int send_buf_size = 1024;
    setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
    setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size));
    return 0;
}

// 接收数据
if ((len = recvfrom(sock, buffer, len, 0, (struct sockaddr *)&addr, &addrlen)) < 0)
{
    if (errno == EAGAIN || errno == EINTR)
    {
        len = 0;
        return 0;
    }
    else
    {
        return -1;
    }
}

 

三、TCP网络通信的实现

 

//Tcp创建
bool CreateTcpServer()
{
     //建立TCP套接字
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    struct sockaddr_in svr_addr= {0};
    svr_addr.sin_port = htons(port);
    svr_addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip.c_str(), &(svr_addr.sin_addr));

    // 绑定ip地址
    if(bind(sock_fd, (struct sockaddr*)&svr_addr, sizeof(struct sockaddr_in)) < 0)

    // 设置socket属性
    int recv_buf_size = 1024 * 10;
    int send_buf_size = 1024 * 10;
    if (setsockopt(_sock_fd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buf_size, sizeof(recv_buf_size)) < 0)
    if(setsockopt(_sock_fd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buf_size, sizeof(send_buf_size)) < 0)
    int reuse=1;
    if(setsockopt(_sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0)

    // 设置非阻塞
    int val = fcntl(_sock_fd, F_GETFL, 0);
    if(fcntl(_sock_fd, F_SETFL, val | O_NONBLOCK) == -1)
    
    // 监听socket
    if(listen(sock_fd,1024) < 0)
    {
        close(sock_fd);
        sock_fd = -1;
        return false;
    }
    return true ;
}
// 一种hash值的生成算法
int string2hash(const char * str, const int& len, const int& hash_size)
{
    int hashvalue = 0;
    for (int i = len-1; i < len; ++i)
    {
        hashvalue = ((unsigned char)(*(str + i)) + hashvalue) % hash_size;
    }
    return hashvalue;
}

//接收数据流程
int TCP_recv()
{
    char recv_buff[1024];
    int total_recv_len = 0;
    
    int clientfd = accept(_sock_fd,(struct sockaddr *)NULL,(socklen_t*)NULL);
    if (clientfd <= 0)
    {
        return -1;
    }

    //设置发送缓冲区和接收缓冲区大小
    int send_buff_size = 1024;
    int recv_buff_size = 1024;
    int set_result = 0;
    set_result = setsockopt(clientfd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buff_size, sizeof(recv_buff_size));
    set_result = setsockopt(clientfd, SOL_SOCKET, SO_SNDBUF, (char*)&send_buff_size, sizeof(send_buff_size));
    
    int free_len = sizeof(recv_buff) - total_recv_len ;
    if(free_len <= 0 || total_recv_len >= 1024)
    {
        //包太长了
        return 0;
    }

    //接收数据
    int recv_len = 0 ;
    do
    {
        recv_len = recv(sock_fd, &recv_buff[total_recv_len], free_len, 0);
    }
    while((recv_len < 0) && (errno == EINTR));

    if(recv_len <= 0)
    {
        // 对端连接关闭
        if( recv_len == 0 )
        {
            return 0;
        }
        else if( errno == EAGAIN)
        {
            //暂时阻塞
            return 0;
        }
        else
        {
            return 0;
        }
    }

    total_recv_len += recv_len;
    // 解析命令
    int offset = ParseCmd(recv_buff, total_recv_len);
    if(0 == offset)
    {
        //未接收完
        return 0;
    }
    // 完成解析后,将已处理后的数据清空,使留下的未处理数据移到缓存区的起始位置。
    else if(0 < offset)
    {
        //解析成功,单条命令
        total_recv_len =  total_recv_len - offset;
        memmove(recv_buff, recv_buff + offset, total_recv_len);
    }

    return 0;

}

// 与服务端建立socket长连接的方法
    int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    int recv_buf_size = 1024;
    int send_buf_size = 1024;

    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size));
    setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());

    int argc = fcntl(fd, F_GETFL, 0);
    if (fcntl(fd, F_SETFL, argc | O_NONBLOCK) == -1)
    {
        close(fd);
        return false;
    }

    int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
    if (0 > ret)
    {
        bool bConnected = false;

        struct pollfd conncet_client[1];
        int _nfd = 1;
        memset(&_conncet_client[0], 0, sizeof(pollfd));
        conncet_client[0].fd = fd;
        conncet_client[0].events = POLLOUT;

        int can_write = ::poll(_conncet_client, _nfd, (int)(1000));
        if (can_write > 0 && (_conncet_client[0].revents & POLLOUT) > 0)
        {
            bConnected = true;
        }
    }

 

posted @ 2019-02-20 19:58  zmlgo  阅读(325)  评论(0编辑  收藏  举报