zoukankan      html  css  js  c++  java
  • 使用libevent和boost编写一个简单的tcp服务器

    写这个东西主要是为了学习libevent的基本用法,以及学习下boost的线程库。

    程序结构比较简单:

      1. 首先是建立一个监听socke。

      2. 将这个监听的socket绑定到一个event事件上,然后等待有客户过来连接。

      3. 如果响应到监听socket可读,则accept尝试连接的客户端。

      4. 开启一个线程来处理所有和这个连接过来的客户端之间的交互。(实际上什么事情也没做,就是cout了下每次recv的数据大小)

    代码如下:

      1. 首先是程序入口,main函数

        main函数主要是注册了一个监听使用的socket。另外一旦进入了监听状态,就不好退出程序,所以一开始就注册了一个信号响应函数,专门用来响应程序退出的信号。

    复制代码
     1 //创建监听socket,然后等待这个socket有客户来链接
     2 //每个链接一个线程去处理
     3 int main(int argc, char* argv[])
     4 {
     5     //首先处理好kill -2信号
     6     struct sigaction sigact = {0};
     7     sigact.sa_sigaction = On_Exit;
     8     if ( -1 == sigaction(2, &sigact, NULL))
     9     {
    10         log("建立响应函数失败");
    11         return 0;
    12     }
    13 
    14     //建立一个非阻塞的socket句柄
    15     int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
    16     if (-1 == sockfd)
    17     {
    18         log("创建监听socket失败", ERROR);
    19         return 0;
    20     }
    21     log("创建监听socket成功");
    22 
    23     //bind
    24     sockaddr_in sinAddr;
    25     sinAddr.sin_family = AF_INET;
    26     sinAddr.sin_port = htons(LISTEN_PORT);
    27     sinAddr.sin_addr.s_addr = inet_addr("172.21.169.160");
    28     if( -1 == bind(sockfd, (sockaddr*)&sinAddr, sizeof(sockaddr_in)))
    29     {
    30         log("监听本地端口失败", ERROR);
    31         return 0;
    32     }
    33 
    34     //进入listen
    35     if (-1 == listen(sockfd, SOMAXCONN))
    36     {
    37         log("监听本地端口失败", ERROR);
    38         return 0;
    39     }
    40     log("监听本地端口成功");
    41 
    42     //进入accept状态
    43     log("即将进入监听状态");
    44     EnterAcceptWhile(sockfd);
    45 
    46     close(sockfd);
    47     log("监听线程成功结束了");
    48     return 0;
    49 }
    复制代码

      2.  进入libevent的消息循环

    复制代码
     1 //基于event的消息响应机制
     2 void EnterAcceptWhile(int nFd)
     3 {
     4    if (g_pEventLoop == NULL)
     5        g_pEventLoop = event_base_new();
     6 
     7    //绑定事件
     8    struct event* pListenEvent = event_new(g_pEventLoop, nFd, EV_PERSIST|EV_READ, On_Sock_Accept, NULL);
     9    if (pListenEvent == NULL)
    10    {
    11        log ("建立监听事件失败");
    12        return;
    13    }
    14 
    15    //加入监听事件,持续
    16    event_add(pListenEvent, NULL);
    17 
    18    //分派消息
    19    int nLoopRst = 0;
    20    if (0 == (nLoopRst = event_base_dispatch(g_pEventLoop)))
    21    {
    22        log("事件循环正常停止了");
    23    }
    24    else if (1 == nLoopRst)
    25    {
    26        log("没有事件关联到这个消息循环了");
    27    }
    28    else
    29    {
    30        log("消息循环出现了错误", ERROR);
    31    }
    32 
    33    event_free(pListenEvent);
    34    event_base_free(g_pEventLoop);
    35    g_pEventLoop = NULL;
    36 }
    复制代码

      3. 一旦响应到有客户端过来连接,就会进入On_Sock_Accept函数。因此这个函数应该尽可能的短小。

    复制代码
     1 //响应客户端连接
     2 void On_Sock_Accept(int nFd, short sFlags, void* pArg)
     3 {
     4     if (!(sFlags&EV_READ))
     5     {
     6         log("接受到了一个莫名其妙的消息", WARNING);
     7         return;
     8     }
     9     log("响应到一个客户端过来连接了"); 
    10 
    11     socklen_t sockLen;
    12     sockaddr sa;
    13     int nAcceptFd = accept(nFd, &sa, &sockLen); 
    14 
    15     //这里应该启动一个线程来处理这个请求事务的,而不应该在这里做大量的复杂操作
    16     SocketThread st(nAcceptFd); 
    17     boost::thread thread_ST(st);
    18 }
    复制代码

      4. 在上个函数中,开了一个线程专门处理来自这个客户端的请求。 类SocketThread的代码如下:

    复制代码
     1 SocketThread::SocketThread(int nFd)
     2 {
     3     m_nFd = nFd;
     4 }
     5 
     6 void SocketThread::operator() ()
     7 {
     8     if (m_nFd == -1)
     9         return;
    10 
    11     //读取数据,等到读取完成之后,输出出来,最后关闭掉socket连接
    12     char* pszTemp = new char[1024];
    13     memset(pszTemp, 0, 1024);
    14 
    15     int nRecvSize = 0;
    16     while( 0 < (nRecvSize = recv(m_nFd, pszTemp, 1024, 0))) 
    17     {
    18         //读到数据了
    19         std::cout << "thread id: " << boost::this_thread::get_id() << " 接受到了:" << nRecvSize << "字节的数据" << std::endl;
    20     }
    21 
    22     delete[] pszTemp;
    23     pszTemp = NULL;
    24 
    25     if (nRecvSize == 0)
    26     {
    27         std::cout << "客户端已经关闭了" << std::endl;
    28     }
    29     else if (nRecvSize < 0)
    30     {
    31         std::cout << "接受客户端数据失败,请检查原因" << std::endl;
    32     }
    33     close(m_nFd);
    34     m_nFd = -1;
    35 }
    复制代码

      5. 最后是响应信号2, 退出libevent的消息循环

      其实这里存在问题:如果退出消息循环的时候还有很多的工作者线程正在运行,应该要先让他们把事情做完再退出的。

    复制代码
     1 //响应退出消息
     2 void On_Exit(int nSigId, siginfo_t* pSigInfo, void* pArg)
     3 {
     4     log("准备结束监听了", WARNING);
     5     if (g_pEventLoop != NULL)
     6     {
     7         //打破监听循环
     8         event_base_loopbreak(g_pEventLoop);
     9     }
    10 }
    复制代码

  • 相关阅读:
    微信小程序捕获async/await函数异常实践
    从微信小程序开发者工具源码看实现原理(四)-
    从微信小程序开发者工具源码看实现原理(二)-
    从微信小程序开发者工具源码看实现原理(三)-
    从微信小程序开发者工具源码看实现原理(一)-
    webpack生成的css文件background-image url图片无法加载
    ubuntu创建快捷方式
    docker 安装常用数据库
    MySQL order by limit 分页数据重复问题
    docker安装redis
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318325.html
Copyright © 2011-2022 走看看