忘记密码   免费注册 查看新帖 |

ChinaUnix.net

  平台 论坛 博客 文库 频道自动化运维 虚拟化 储存备份 C/C++ PHP MySQL 嵌入式 Linux系统
最近访问板块 发新帖
查看: 2554 | 回复: 1

[Linux] 关于epoll水平触发的小问题 [复制链接]

论坛徽章:
0
发表于 2018-06-12 20:20 |显示全部楼层
本帖最后由 fuyuande 于 2018-06-12 20:28 编辑

根据相关的资料得知,当epoll监听的对象设置为水平触发时(默认是水平触发),当发生可读事件并且没有将数据读取完毕,epoll会多次触发,直到数据读完,这一点在测试的监听标准输入的时候得到验证。代码如下:
  1. #include <sys/epoll.h>
  2. #include <sys/types.h>
  3. #include <sys/errno.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <stdio.h>

  8. #define BUFFER_SIZE 512
  9. #define log(fmt, arg...) printf(""fmt, ##arg)

  10. void main(){
  11.     int efd, fds, i, fd;
  12.     int ret;
  13.     struct epoll_event g_event;             // epoll事件
  14.     struct epoll_event *epoll_events_ptr;
  15.     char buffer[BUFFER_SIZE] = {0};

  16.     efd = epoll_create1(0);                 //创建epoll实例
  17.     if (efd == -1) {
  18.         log("create epoll fail \r\n");
  19.         goto err;

  20.     }
  21.     log("create epoll instance success \r\n");
  22.    
  23.     epoll_events_ptr = (struct epoll_event *)calloc(2, sizeof(struct epoll_event));
  24.     if (epoll_events_ptr == NULL) {
  25.         log("calloc fail \r\n");
  26.         goto err;
  27.     }


  28.     g_event.data.fd = STDIN_FILENO; //监听标准输入
  29.     g_event.events = EPOLLIN;                               //默认水平触发
  30.     epoll_ctl(efd, EPOLL_CTL_ADD, STDIN_FILENO, &g_event);         

  31.     while(1) {                                              //监听epoll事件
  32.         fds = epoll_wait(efd, epoll_events_ptr, 2, -1);
  33.         for (i = 0; i<fds; i++)
  34.         {   
  35.             if (epoll_events_ptr[i].events & EPOLLIN)
  36.             {   
  37.                 ret = read(STDIN_FILENO, buffer, 1);
  38.                 if(ret != -1)
  39.                     log("recv msg : %s \n", buffer);
  40.             }     
  41.             memset(buffer, 0, BUFFER_SIZE);
  42.         }        
  43.     }   

  44.    
  45. err:
  46.     if(epoll_events_ptr)
  47.         free(epoll_events_ptr);

  48.     return ;

  49. }


复制代码

epoll_lt_stdin.png
但是测试监听udp套接字的时候,当向套接字发送多个数据,每次只读取一个,epoll只触发了一次,同样是水平触发,不清楚为什么udp套接字没有多次触发,代码:
  1. #include <netinet/in.h>
  2. #include <sys/socket.h>
  3. #include <sys/epoll.h>
  4. #include <sys/types.h>
  5. #include <arpa/inet.h>
  6. #include <sys/errno.h>
  7. #include <stdint.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <stdio.h>

  11. #define BUFFER_SIZE 512
  12. #define log(fmt, arg...) printf(""fmt, ##arg)

  13. void main(){
  14.     int fd1,efd, fds, i, fd;
  15.     int ret, addr_len;
  16.     struct epoll_event g_event;             // epoll事件
  17.     struct epoll_event *epoll_events_ptr;
  18.     char buffer[BUFFER_SIZE] = {0};
  19.     struct sockaddr_in addr1;

  20.     fd1 = socket(AF_INET, SOCK_DGRAM, 0);   //创建套接字
  21.     if (fd1 == -1) {
  22.         log("create socket fail \r\n");
  23.         return ;
  24.     }     

  25.     addr1.sin_family = AF_INET;             //设置本地地址与端口
  26.     addr1.sin_addr.s_addr = INADDR_ANY;
  27.     addr1.sin_port = htons(3500);   
  28.     addr_len = sizeof(struct sockaddr_in);
  29.    
  30.     if (0 != bind(fd1, (struct sockaddr *)&addr1, sizeof(struct sockaddr_in))) {
  31.         log("bind local listening addr fail,errno : %d \r\n", errno);
  32.         goto err;
  33.     }


  34.     efd = epoll_create1(0);                 //创建epoll实例
  35.     if (efd == -1) {
  36.         log("create epoll fail \r\n");
  37.         goto err;

  38.     }
  39.     log("create epoll instance success \r\n");
  40.    
  41.     epoll_events_ptr = (struct epoll_event *)calloc(2, sizeof(struct epoll_event));
  42.     if (epoll_events_ptr == NULL) {
  43.         log("calloc fail \r\n");
  44.         goto err;
  45.     }


  46.     g_event.data.fd = fd1;
  47.     g_event.events = EPOLLIN | EPOLLET;   //水平触发
  48.     epoll_ctl(efd, EPOLL_CTL_ADD, fd1, &g_event);         

  49.     while(1) {
  50.         fds = epoll_wait(efd, epoll_events_ptr, 2, -1); //阻塞
  51.         for (i = 0; i<fds; i++)
  52.         {   
  53.             if (epoll_events_ptr[i].events & EPOLLIN)
  54.             {   
  55.                 ret = read(fd1, buffer, 1);
  56.                 if(ret != -1)
  57.                     log("recv msg : %s \n", buffer);
  58.                 }     
  59.             memset(buffer, 0, BUFFER_SIZE);
  60.         }        
  61.     }   

  62.    
  63. err:
  64.     close(fd1);
  65.     if(epoll_events_ptr)
  66.         free(epoll_events_ptr);

  67.     return ;

  68. }

复制代码

监听udp套接字

监听udp套接字


当向套接字发送3个字节的时候,只读取一个,按道理说epoll应该触发三次,可是这里只触发了一次。
有熟悉的大佬讲一下


论坛徽章:
0
发表于 2018-06-20 13:03 |显示全部楼层
问题原因清楚了。在stackoverflow上提问,有人回答说是因为UDP套接字如果一次没有读完,剩余数据会被丢弃,所以epoll只触发了一次。UDP套接字和面向字节流的TCP不同,它是以报文为单位的。实际测试了一下也验证了该结论。果然对套接字了解不够多啊。相关链接如下:stackoverflow
您需要登录后才可以回帖 登录 | 注册

本版积分规则

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号 北京市公安局海淀分局网监中心备案编号:11010802020122
广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员  联系我们:wangnan@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP