0. 前言

  这篇文章主要记录在使用epoll实现NIO接入时所遇到的问题。

1. epoll简介

  epoll是Linux下提供的NIO,其主要有两种模式,ET(Edge trige)和LT(Level trige)。在linux下使用man epoll手册即可知道这两种模式主要的区别:

  ET:边缘触发,故名思议,所添加的描述符,只在当其改变状态的时候才会触发一次,就如同数电里面电平的边缘触发。

  在man里面列举了一个例子,当一个fd添加到epoll中时,当有2KB数据到达时,epoll_wait会返回事件个数以及其fd,此时,当程序读取了1KB数据后继续调用epoll_wait,1)对于ET来说会继续等待,2)而LT则是继续触发返回。

 

2. epoll错误程序分析

  下述为错误的示例:

  1 #include <sys/epoll.h>
  2 #include <unistd.h>
  3 #include <fcntl.h>
  4 #include <sys/types.h>
  5 #include <sys/socket.h>
  6 #include <errno.h>
  7 #include <string.h>
  8 
  9 #define MAX_BACKLOG    256
 10 #define MAX_EVENTS    1024
 11 
 12 static int SetNonBlock(int nFd)
 13 {
 14     int nOldOpt = fcntl(nFd, F_GETFD, 0);
 15     int nNewOpt = nOldOpt | O_NONBLOCK;
 16 
 17     return fcntl(nFd, F_SETFD, nNewOpt);
 18 }
 19 
 20 static void AddEvent(int nEpfd, int nFd)
 21 {
 22     struct epoll_event event;
 23     event.data.fd = nFd;
 24     event.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET;
 25 
 26     return epoll_ctl(nEpfd, EPOLL_CTL_ADD, &event);
 27 }
 28 
 29 int main(int argc, char const *argv[])
 30 {
 31     if (argc != 2)
 32     {
 33         printf("usage: CMD Port\n");
 34         exit(-1);
 35     }
 36 
 37     int nPort = atoi(argv[1]);
 38     if (nPort < 0)
 39     {
 40         printf("Port Invalid\n");
 41         exit(-1);
 42     }
 43 
 44     int nSvrFd = socket(AF_INET, SOCK_STREAM, 0);
 45     //设置非阻塞
 46     SetNonBlock(nSvrFd);
 47 
 48     //绑定地址
 49     struct sockaddr_in addr;
 50     bzero(&addr, sizeof(addr));
 51 
 52     addr.sin_addr = 0;
 53     addr.sin_port = htons(argv[1]);
 54     addr.sin_family = htos(AF_INET);
 55 
 56     if (0 != bind(nSvrFd, (struct sockaddr*)&addr, sizeof(addr)))
 57     {
 58         perror("Bind Failure:");
 59         exit(-1);
 60     }
 61 
 62     //监听端口
 63     if (0 != listen(nSvrFd, MAX_BACKLOG))
 64     {
 65         perror("Listen Failure:");
 66         exit(-1);
 67     }
 68 
 69     int nEpfd = epoll_create(1024);
 70     struct epoll_event events[MAX_EVENTS];
 71 
 72     AddEvent(nEpfd, nSvrFd);
 73 
 74     while (1)
 75     {
 76         //等待事件到来
 77         int nReadyNums = epoll_wait(nEpfd, events, MAX_EVENTS, -1);
 78 
 79         for (int i = 0; i < nReadyNums; ++i)
 80         {
 81             if (events[i].data.fd == nSvrFd)
 82             {
 83                 //这里对于ET模式来说是有问题的
 84                 int nClientFd = accept(nSvrFd, NULL, NULL);
 85                 if (-1 != nClientFd)
 86                 {
 87                     //设置为非阻塞
 88                     SetNonBlock(nClientFd);
 89                     //添加事件监听
 90                     AddEvent(nEpfd, nClientFd);
 91                 }
 92 
 93             }  else
 94             {
 95                 //处理FD事件
 96             }
 97         }
 98     }
 99 
100     return 0;
101 }
View Code

相关文章: