免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 7080 | 回复: 4
打印 上一主题 下一主题

谁能够帮我解释一下,linux下使用epoll的例子 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-12-27 17:08 |只看该作者 |倒序浏览
10可用积分

谁能够帮我解释一下,linux下使用epoll的例子,这个例子中,兰山部分没有看明白:

为什么收到“EPOLLIN”,即有数据需要读取,read之后,还要修改要处理的事件为EPOLLOUT呢?



for ( ; ; ) {
          //等待epoll事件的发生
          nfds=epoll_wait(epfd,events,20,500);
          //处理所发生的所有事件      
          for(i=0;i<nfds;++i)
          {
               if(events.data.fd==listenfd)
               {

                    connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
                    if(connfd<0){
                         perror("connfd<0");
                         exit(1);
                    }
                    setnonblocking(connfd);

                    char *str = inet_ntoa(clientaddr.sin_addr);
                    std::cout<<"connect from "<_u115 ?tr<<std::endl;
                    //设置用于读操作的文件描述符
                    ev.data.fd=connfd;
                    //设置用于注测的读操作事件
                    ev.events=EPOLLIN|EPOLLET;
                    //注册ev
                    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
               }
               else if(events.events&EPOLLIN)
               {
                    if ( (sockfd = events.data.fd) < 0) continue;
                    if ( (n = read(sockfd, line, MAXLINE)) < 0) {
                         if (errno == ECONNRESET) {

                              close(sockfd);
                              events.data.fd = -1;
                         } else
                              std::cout<<"readline error"<<std::endl;
                    } else if (n == 0) {
                         close(sockfd);
                         events.data.fd = -1;
                    }
                    //设置用于写操作的文件描述符
                    ev.data.fd=sockfd;
                    //设置用于注测的写操作事件
                    ev.events=EPOLLOUT|EPOLLET;
                    //修改sockfd上要处理的事件为EPOLLOUT
                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
               }
               else if(events.events&EPOLLOUT)
               {   
                    sockfd = events.data.fd;
                    write(sockfd, line, n);
                    //设置用于读操作的文件描述符
                    ev.data.fd=sockfd;
                    //设置用于注测的读操作事件
                    ev.events=EPOLLIN|EPOLLET;
                    //修改sockfd上要处理的事件为EPOLIN
                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
               }

          }

     }
}

论坛徽章:
0
2 [报告]
发表于 2007-12-27 17:40 |只看该作者
我的博客中有点东西,或许对你理解epoll有帮助

论坛徽章:
0
3 [报告]
发表于 2007-12-28 13:27 |只看该作者
这个需要根据具体情况使用
如果这个fd你只读不写,完全可以不用改为EPOLLOUT

看我收集的这个模型,更容易理解一些:

  1. #include <iostream>
  2. #include <sys/socket.h>
  3. #include <sys/epoll.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <stdio.h>
  9. #include <pthread.h>

  10. #define MAXLINE 10

  11. #define OPEN_MAX 100

  12. #define LISTENQ 20

  13. #define SERV_PORT 5555

  14. #define INFTIM 1000


  15. //线程池任务队列结构体

  16. struct task{
  17.   int fd;            //需要读写的文件描述符

  18.   struct task *next; //下一个任务

  19. };

  20. //用于读写两个的两个方面传递参数

  21. struct user_data{
  22.   int fd;
  23.   unsigned int n_size;
  24.   char line[MAXLINE];
  25. };

  26. //线程的任务函数

  27. void * readtask(void *args);

  28. void * writetask(void *args);


  29. //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件

  30. struct epoll_event ev,events[20];

  31. int epfd;

  32. pthread_mutex_t mutex;

  33. pthread_cond_t cond1;

  34. struct task *readhead=NULL,*readtail=NULL,*writehead=NULL;


  35. void setnonblocking(int sock)
  36. {
  37.      int opts;
  38.      opts=fcntl(sock,F_GETFL);
  39.      if(opts<0)
  40.      {
  41.           perror("fcntl(sock,GETFL)");
  42.           exit(1);
  43.      }
  44.     opts = opts|O_NONBLOCK;
  45.      if(fcntl(sock,F_SETFL,opts)<0)
  46.      {
  47.           perror("fcntl(sock,SETFL,opts)");
  48.           exit(1);
  49.      }   
  50. }


  51. int main()
  52. {
  53.      int i, maxi, listenfd, connfd, sockfd,nfds;
  54.      pthread_t tid1,tid2;
  55.    
  56.      struct task *new_task=NULL;
  57.      struct user_data *rdata=NULL;
  58.      socklen_t clilen;
  59.    
  60.      pthread_mutex_init(&mutex,NULL);
  61.      pthread_cond_init(&cond1,NULL);
  62.      //初始化用于读线程池的线程

  63.      pthread_create(&tid1,NULL,readtask,NULL);
  64.      pthread_create(&tid2,NULL,readtask,NULL);
  65.    
  66.      //生成用于处理accept的epoll专用的文件描述符   
  67.      epfd=epoll_create(256);

  68.      struct sockaddr_in clientaddr;
  69.      struct sockaddr_in serveraddr;
  70.      listenfd = socket(AF_INET, SOCK_STREAM, 0);
  71.      //把socket设置为非阻塞方式

  72.      setnonblocking(listenfd);
  73.      //设置与要处理的事件相关的文件描述符

  74.      ev.data.fd=listenfd;
  75.      //设置要处理的事件类型

  76.      ev.events=EPOLLIN|EPOLLET;
  77.      //注册epoll事件

  78.      epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
  79.    
  80.      bzero(&serveraddr, sizeof(serveraddr));
  81.      serveraddr.sin_family = AF_INET;
  82.    
  83.      char *local_addr="200.200.200.222";
  84.      inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);
  85.      serveraddr.sin_port=htons(SERV_PORT);
  86.      bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
  87.      listen(listenfd, LISTENQ);
  88.    
  89.      maxi = 0;
  90.      for ( ; ; ) {
  91.           //等待epoll事件的发生

  92.           nfds=epoll_wait(epfd,events,20,500);
  93.           //处理所发生的所有事件     
  94.         for(i=0;i<nfds;++i)
  95.         {
  96.                if(events[i].data.fd==listenfd)
  97.                {
  98.                   
  99.                     connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
  100.                     if(connfd<0){
  101.                       perror("connfd<0");
  102.                       exit(1);
  103.                    }
  104.                     setnonblocking(connfd);
  105.                   
  106.                     char *str = inet_ntoa(clientaddr.sin_addr);
  107.                     std::cout<<"connec_ from >>"<<str<<std::endl;
  108.                     //设置用于读操作的文件描述符

  109.                     ev.data.fd=connfd;
  110.                     //设置用于注测的读操作事件

  111.                  ev.events=EPOLLIN|EPOLLET;
  112.                     //注册ev

  113.                  epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
  114.                }
  115.             else if(events[i].events&EPOLLIN)
  116.             {
  117.                     printf("reading!\n");               
  118.                     if ( (sockfd = events[i].data.fd) < 0) continue;
  119.                     new_task=new task();
  120.                     new_task->fd=sockfd;
  121.                     new_task->next=NULL;
  122.                     //添加新的读任务

  123.                     pthread_mutex_lock(&mutex);
  124.                     if(readhead==NULL)
  125.                     {
  126.                       readhead=new_task;
  127.                       readtail=new_task;
  128.                     }   
  129.                     else

  130.                     {   
  131.                      readtail->next=new_task;
  132.                       readtail=new_task;
  133.                     }   
  134.                    //唤醒所有等待cond1条件的线程

  135.                     pthread_cond_broadcast(&cond1);
  136.                     pthread_mutex_unlock(&mutex);  
  137.               }
  138.                else if(events[i].events&EPOLLOUT)
  139.                {   
  140.               rdata=(struct user_data *)events[i].data.ptr;
  141.                  sockfd = rdata->fd;
  142.                  write(sockfd, rdata->line, rdata->n_size);
  143.                  delete rdata;
  144.                  //设置用于读操作的文件描述符

  145.                  ev.data.fd=sockfd;
  146.                  //设置用于注测的读操作事件

  147.                ev.events=EPOLLIN|EPOLLET;
  148.                  //修改sockfd上要处理的事件为EPOLIN

  149.                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
  150.                }
  151.                              
  152.           }
  153.          
  154.      }
  155. }

  156. void * readtask(void *args)
  157. {
  158.    
  159.    int fd=-1;
  160.    unsigned int n;
  161.    //用于把读出来的数据传递出去

  162.    struct user_data *data = NULL;
  163.    while(1){
  164.         
  165.         pthread_mutex_lock(&mutex);
  166.         //等待到任务队列不为空

  167.         while(readhead==NULL)
  168.              pthread_cond_wait(&cond1,&mutex);
  169.         
  170.         fd=readhead->fd;
  171.         //从任务队列取出一个读任务

  172.         struct task *tmp=readhead;
  173.         readhead = readhead->next;
  174.         delete tmp;
  175.         pthread_mutex_unlock(&mutex);
  176.         data = new user_data();
  177.         data->fd=fd;
  178.         if ( (n = read(fd, data->line, MAXLINE)) < 0) {
  179.            
  180.            if (errno == ECONNRESET) {
  181.              close(fd);
  182.             
  183.           } else

  184.              std::cout<<"readline error"<<std::endl;
  185.            if(data!=NULL)delete data;
  186.         } else if (n == 0) {
  187.             close(fd);
  188.            printf("Client close connect!\n");
  189.            if(data!=NULL)delete data;
  190.         } else{
  191.         
  192.         data->n_size=n;
  193.         //设置需要传递出去的数据

  194.         ev.data.ptr=data;
  195.         //设置用于注测的写操作事件

  196.         ev.events=EPOLLOUT|EPOLLET;
  197.         //修改sockfd上要处理的事件为EPOLLOUT

  198.         epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
  199.        }
  200.    }
  201. }


复制代码

论坛徽章:
0
4 [报告]
发表于 2007-12-28 13:30 |只看该作者
恩,和具体实现是有关的.

论坛徽章:
0
5 [报告]
发表于 2008-01-15 16:21 |只看该作者
爆发式的连接,并且客户端都活跃时,如果用LT模式,两个vepoll_wait之间时间会比较长。
可以采用一个线程用一个select来处理listenhandle,另外一个线程用epoll来处理余下的客户端。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP