关于epoll的一些心得----关于监听和数据处理

来源:互联网 发布:kindle哪个版本好 知乎 编辑:程序博客网 时间:2024/05/21 20:36

      最近开始接触Linux,那么必然的也开始接触epoll.

      epoll其实很简单,就三个函数epoll_create, epoll_ctl和epoll_wait。当有网络事件触发的时候由内核主动通知应用程序,它只会对“活跃”的socket进行操作。- -!都是废话,下面进入正题(以ET来说)。

      下面是主要的处理线程(有些是伪代码 大概意思就是这样)。

    int fd_count;
    SOCKET  sock;

      while(1)
     {

          //等待一个网络事件
          fd_count = epoll_wait(mgr->epoll_fd, events, THREAD_EVENT_SIZE, 5000);
          for(i = 0; i < fd_count; ++i)
          {//fd_count是有多少个事件被触发
                      sock = events[i].data.fd;

                     if(sock == INVALID_SOCK)
                    {

                          continue;

                     }
                     if( sock == listenfds[events[i].data.fd]))
                     {//listenfds是监听socket的数组用sock做索引
                                OnAccept(sock ); 

                                //PostEvent是epoll_ctl
                                 /*PostEvent(sock,EPOLLIN | EPOLLET); */<--------------------------------------------1

 
                     }
                         

                     if(events[i].events & EPOLLHUP || events[i].events & EPOLLERR)
                    {
                             Disconnect(sock);
                             continue;
                     }
                    else if(events[i].events & EPOLLIN)
                   {//读事件
                              ReadCallback(0);               // 触发回调函数.

   
                              if(CanSend(sock))
                                    PostEvent(sock,EPOLLOUT);<--------------------------------------------2
  
                              //  PostEvent(sock, EPOLLIN | EPOLLET); //<--------------------------------3

                  }
                  else if(events[i].events & EPOLLOUT)
                 {//写事件
                            WriteCallback();       // 写回调
                            PostEvent(sock ,EPOLLIN);  //<---------------------------------------------------4
                }
            }
              
    }

 

      以下的测试环境为 64位Redhat 双核 1G内存。

      这是一个比较常见的模型,先说监听吧. <------1处屏蔽的时候会发生这么个情况,同时有2个连接事件发生的时候 有一个连接不会被处理,这是ET本身的规则所导致,它认为已经通知过你有事件到达了,至于怎么处理完 那是你的事。处理接收还有个用的比较常用的方法,就是起一个接收线程,但是这样有一个坏处---当一个应用程序有多个接收sock的时候会启动多个接收线程,会带来额外的线程切换开销。在测试建立连接的时候同时起了8个客户端(5台机器),建立6w个连接,发现<------1处如果屏蔽了处理速度非常慢,而且还有不能处理的连接,而接收线程的方式和放开<-------1的方式速度差不多,也不存在连接丢失的情况。

      <------2  <--------3  <-------4 说的其实是一个问题,刚开始的时候<------2 和 <--------4 处其实是屏蔽的 <--------3 处放开,因为我想处理完一个recv事件后应该继续投递下一个recv事件,这样才能把所有数据读完而投递写事件没有什么用。在简单的测试程序里这样是没有什么问题,但是放在应用的工程里发现了毛病.一般正常严谨的工程里,发送数据应该是将数据写在一个缓存里然后投递写事件就不管了,那么这样问题就出来了,你投递的着个 写事件 或者 <------3处的读事件并不会共存,而会被后来者覆盖,即epoll_ctl EPOLLOUT后在消息还没被处理的时候又投递了个EPOLLIN,那么EPOLLOUT将不会被触发了。而屏蔽<------3 打开<------2和 <--------4 则2边的事件始终都能被执行。

     呃...大概就是这样在后来的测试中基本程序算是比较稳定的,没有出现过丢包和丢连接的现象。

     PS:关于测试的客户端,一台机器的连接数其实是有限制的,1是会有65535的端口限制(客户端的端口其实也是默认给分配的),2是每个连接会占用一定得非分页内存,这样一台硬件机器客户端的连接数不会非常大。