epoll的使用方法

那么究竟如何来使用epoll呢?其实非常简单。

 

通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

 

首先通过create_epoll(int maxfds)来创建一个epoll的句柄。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

 

之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。基本的语法为:

nfds = epoll_wait(kdpfd, events, maxevents, -1);

 

其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件返回,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。

 

epoll_wait返回之后应该是一个循环,遍历所有的事件。

 

 

几乎所有的epoll程序都使用下面的框架:

[cpp] view plaincopyprint?

  1. for( ; ; )  

  2.    {  

  3.        nfds = epoll_wait(epfd,events,20,500);  

  4.        for(i=0;i<nfds;++i)  

  5.        {  

  6.            if(events[i].data.fd==listenfd) //有新的连接  

  7.            {  

  8.                connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept这个连接  

  9.                ev.data.fd=connfd;  

  10.                ev.events=EPOLLIN|EPOLLET;  

  11.                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //将新的fd添加到epoll的监听队列中  

  12.            }  

  13.   

  14.            else if( events[i].events&EPOLLIN ) //接收到数据,读socket  

  15.            {  

  16.                n = read(sockfd, line, MAXLINE)) < 0    //读  

  17.                ev.data.ptr = md;     //md为自定义类型,添加数据  

  18.                ev.events=EPOLLOUT|EPOLLET;  

  19.                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓  

  20.            }  

  21.            else if(events[i].events&EPOLLOUT) //有数据待发送,写socket  

  22.            {  

  23.                struct myepoll_data* md = (myepoll_data*)events[i].data.ptr;    //取数据  

  24.                sockfd = md->fd;  

  25.                send( sockfd, md->ptr, strlen((char*)md->ptr), 0 );        //发送数据  

  26.                ev.data.fd=sockfd;  

  27.                ev.events=EPOLLIN|EPOLLET;  

  28.                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据  

  29.            }  

  30.            else  

  31.            {  

  32.                //其他的处理  

  33.            }  

  34.        }  

  35.    }  


epoll的程序实例

[cpp] view plaincopyprint?

  1.  #include <stdio.h>  

  2. #include <stdlib.h>  

  3. #include <unistd.h>  

  4. #include <errno.h>  

  5. #include <sys/socket.h>  

  6. #include <netdb.h>  

  7. #include <fcntl.h>  

  8. #include <sys/epoll.h>  

  9. #include <string.h>  

  10.   

  11. #define MAXEVENTS 64    

  12. //函数:  

  13. //功能:创建和绑定一个TCP socket  

  14. //参数:端口  

  15. //返回值:创建的socket  

  16. static int  

  17. create_and_bind (char *port)  

  18. {  

  19.   struct addrinfo hints;  

  20.   struct addrinfo *result, *rp;  

  21.   int s, sfd;  

  22.   

  23.   memset (&hints, 0, sizeof (struct addrinfo));  

  24.   hints.ai_family = AF_UNSPEC;     /* Return IPv4 and IPv6 choices */  

  25.   hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */  

  26.   hints.ai_flags = AI_PASSIVE;     /* All interfaces */  

  27.   

  28.   s = getaddrinfo (NULL, port, &hints, &result);  

  29.   if (s != 0)  

  30.     {  

  31.       fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));  

  32.       return -1;  

  33.     }  

  34.   

  35.   for (rp = result; rp != NULL; rp = rp->ai_next)  

  36.     {  

  37.       sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);  

  38.       if (sfd == -1)  

  39.         continue  

  40.       s = bind (sfd, rp->ai_addr, rp->ai_addrlen);  

  41.       if (s == 0)  

  42.         {  

  43.           /* We managed to bind successfully! */  

  44.           break;  

  45.         }  

  46.   

  47.       close (sfd);  

  48.     }  

  49.   

  50.   if (rp == NULL)  

  51.     {  

  52.       fprintf (stderr, "Could not bind\n");  

  53.       return -1;  

  54.     }  

  55.   

  56.   freeaddrinfo (result);  

  57.   

  58.   return sfd;  

  59. }    

  60. //函数  

  61. //功能:设置socket为非阻塞的  

  62. static int  

  63. make_socket_non_blocking (int sfd)  

  64. {  

  65.   int flags, s;  

  66.   

  67.   //得到文件状态标志  

  68.   flags = fcntl (sfd, F_GETFL, 0);  

  69.   if (flags == -1)  

  70.     {  

  71.       perror ("fcntl");  

  72.       return -1;  

  73.     }  

  74.   

  75.   //设置文件状态标志  

  76.   flags |= O_NONBLOCK;  

  77.   s = fcntl (sfd, F_SETFL, flags);  

  78.   if (s == -1)  

  79.     {  

  80.       perror ("fcntl");  

  81.       return -1;  

  82.     }    

  83.   return 0;  

  84. }    

  85. //端口由参数argv[1]指定  

  86. int  

  87. main (int argc, char *argv[])  

  88. {  

  89.   int sfd, s;  

  90.   int efd;  

  91.   struct epoll_event event;  

  92.   struct epoll_event *events;  

  93.   

  94.   if (argc != 2)  

  95.     {  

  96.       fprintf (stderr, "Usage: %s [port]\n", argv[0]);  

  97.       exit (EXIT_FAILURE);  

  98.     }  

  99.   

  100.   sfd = create_and_bind (argv[1]);  

  101.   if (sfd == -1)  

  102.     abort ();  

  103.   

  104.   s = make_socket_non_blocking (sfd);  

  105.   if (s == -1)  

  106.     abort ();  

  107.   

  108.   s = listen (sfd, SOMAXCONN);  

  109.   if (s == -1)  

  110.     {  

  111.       perror ("listen");  

  112.       abort ();  

  113.     }  

  114.   

  115.   //除了参数size被忽略外,此函数和epoll_create完全相同  

  116.   efd = epoll_create1 (0);  

  117.   if (efd == -1)  

  118.     {  

  119.       perror ("epoll_create");  

  120.       abort ();  

  121.     }   

  122.   event.data.fd = sfd;  

  123.   event.events = EPOLLIN | EPOLLET;//读入,边缘触发方式  

  124.   s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);  

  125.   if (s == -1)  

  126.     {  

  127.       perror ("epoll_ctl");  

  128.       abort ();  

  129.     }    

  130.   /* Buffer where events are returned */  

  131.   events = calloc (MAXEVENTS, sizeof event);    

  132.   /* The event loop */  

  133.   while (1)  

  134.     {  

  135.       int n, i;  

  136.   

  137.       n = epoll_wait (efd, events, MAXEVENTS, -1);  

  138.       for (i = 0; i < n; i++)  

  139.         {  

  140.           if ((events[i].events & EPOLLERR) ||  

  141.               (events[i].events & EPOLLHUP) ||  

  142.               (!(events[i].events & EPOLLIN)))  

  143.             {  

  144.               /* An error has occured on this fd, or the socket is not 

  145.                  ready for reading (why were we notified then?) */  

  146.               fprintf (stderr, "epoll error\n");  

  147.               close (events[i].data.fd);  

  148.               continue;  

  149.             }    

  150.           else if (sfd == events[i].data.fd)  

  151.             {  

  152.               /* We have a notification on the listening socket, which 

  153.                  means one or more incoming connections. */  

  154.               while (1)  

  155.                 {  

  156.                   struct sockaddr in_addr;  

  157.                   socklen_t in_len;  

  158.                   int infd;  

  159.                   char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];  

  160.   

  161.                   in_len = sizeof in_addr;  

  162.                   infd = accept (sfd, &in_addr, &in_len);  

  163.                   if (infd == -1)  

  164.                     {  

  165.                       if ((errno == EAGAIN) ||  

  166.                           (errno == EWOULDBLOCK))  

  167.                         {  

  168.                           /* We have processed all incoming 

  169.                              connections. */  

  170.                           break;  

  171.                         }  

  172.                       else  

  173.                         {  

  174.                           perror ("accept");  

  175.                           break;  

  176.                         }  

  177.                     }   

  178.                                   //将地址转化为主机名或者服务名  

  179.                   s = getnameinfo (&in_addr, in_len,  

  180.                                    hbuf, sizeof hbuf,  

  181.                                    sbuf, sizeof sbuf,  

  182.                                    NI_NUMERICHOST | NI_NUMERICSERV);//flag参数:以数字名返回  

  183.                                   //主机地址和服务地址  

  184.   

  185.                   if (s == 0)  

  186.                     {  

  187.                       printf("Accepted connection on descriptor %d "  

  188.                              "(host=%s, port=%s)\n", infd, hbuf, sbuf);  

  189.                     }  

  190.   

  191.                   /* Make the incoming socket non-blocking and add it to the 

  192.                      list of fds to monitor. */  

  193.                   s = make_socket_non_blocking (infd);  

  194.                   if (s == -1)  

  195.                     abort ();  

  196.   

  197.                   event.data.fd = infd;  

  198.                   event.events = EPOLLIN | EPOLLET;  

  199.                   s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);  

  200.                   if (s == -1)  

  201.                     {  

  202.                       perror ("epoll_ctl");  

  203.                       abort ();  

  204.                     }  

  205.                 }  

  206.               continue;  

  207.             }  

  208.           else  

  209.             {  

  210.               /* We have data on the fd waiting to be read. Read and 

  211.                  display it. We must read whatever data is available 

  212.                  completely, as we are running in edge-triggered mode 

  213.                  and won't get a notification again for the same 

  214.                  data. */  

  215.               int done = 0;  

  216.   

  217.               while (1)  

  218.                 {  

  219.                   ssize_t count;  

  220.                   char buf[512];  

  221.   

  222.                   count = read (events[i].data.fd, buf, sizeof(buf));  

  223.                   if (count == -1)  

  224.                     {  

  225.                       /* If errno == EAGAIN, that means we have read all 

  226.                          data. So go back to the main loop. */  

  227.                       if (errno != EAGAIN)  

  228.                         {  

  229.                           perror ("read");  

  230.                           done = 1;  

  231.                         }  

  232.                       break;  

  233.                     }  

  234.                   else if (count == 0)  

  235.                     {  

  236.                       /* End of file. The remote has closed the 

  237.                          connection. */  

  238.                       done = 1;  

  239.                       break;  

  240.                     }  

  241.   

  242.                   /* Write the buffer to standard output */  

  243.                   s = write (1, buf, count);  

  244.                   if (s == -1)  

  245.                     {  

  246.                       perror ("write");  

  247.                       abort ();  

  248.                     }  

  249.                 }  

  250.   

  251.               if (done)  

  252.                 {  

  253.                   printf ("Closed connection on descriptor %d\n",  

  254.                           events[i].data.fd);  

  255.   

  256.                   /* Closing the descriptor will make epoll remove it 

  257.                      from the set of descriptors which are monitored. */  

  258.                   close (events[i].data.fd);  

  259.                 }  

  260.             }  

  261.         }  

  262.     }    

  263.   free (events);  

  264.   

  265.   close (sfd);  

  266.   

  267.   return EXIT_SUCCESS;  

  268. }  

转载出处:http://blog.chinaunix.net/uid-24517549-id-4051156.html