免费注册 查看新帖 |

Chinaunix

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

[C] epoll server, 客户端关闭时陷入死循环 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-04-07 20:28 |只看该作者 |倒序浏览
本帖最后由 akwhole 于 2013-04-07 21:25 编辑

下午测试EPOLLIN、EPOLLOUT的用法.

服务端关键代码如下:
  1. for(i=0; i < nfds; i++)
  2. {
  3.         fd = events[i].data.fd;
  4.         printf("event is %d\n",events[i].events);

  5.         if(fd == listen_fd)
  6.         {
  7.                         conn_sock = accept(listen_fd, (struct sockaddr *)&addr_client, &addrlen);
  8.                         if(conn_sock < 0)
  9.                         {
  10.                                         perror("[main]accept error1");
  11.                                         exit(1);
  12.                         }
  13.                         set_non_block(conn_sock);
  14.                         ev.events = EPOLLET | EPOLLIN;
  15.                         ev.data.fd = conn_sock;
  16.                         if(-1 == epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &ev))
  17.                         {      
  18.                                         perror("[main]epoll ctl failed!");
  19.                                         exit(1);
  20.                         }      
  21.                         printf("client IP:%s Port:%d connect success!\n",inet_ntoa(addr_client.sin_addr),addr_client.sin_port);
  22.         }  
  23.         else if(events[i].events & EPOLLIN)
  24.         {      
  25.                 char buff[50] = {0};
  26.                 read(fd, buff, 50);

  27.                 printf("EPOLL EVENTS %d\n", events[i].events);
  28.                 printf("EPOLLIN\n");
  29.                 events[i].events = EPOLLOUT | EPOLLET;
  30.                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &events[i]);

  31.                 printf("%s\n",buff);
  32.                 printf("change to EPOLLOUT\n");
  33.                 /*      
  34.                 if(strlen(buff) == 0)
  35.                 {
  36.                         close(fd);
  37.                 }
  38.                 */  
  39.         }      
  40.         else if(events[i].events & EPOLLOUT)
  41.         {      
  42.                 printf("EPOLL EVENTS %d\n", events[i].events);
  43.                 printf("EPOLLOUT\n");
  44.                 events[i].events = EPOLLIN | EPOLLET;
  45.                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &events[i]);
  46.                 printf("change to EPOLLIN\n");
  47.         }
  48.         printf("next chu fa\n");
  49. }
复制代码
客户端代码比较简单
  1. int main()
  2. {
  3.         int connect_fd;
  4.         int ret;
  5.         int port = 50105;
  6.         struct sockaddr_in srv_addr;

  7.         connect_fd = socket(AF_INET, SOCK_STREAM, 0);

  8.         if(connect_fd < 0)
  9.         {      
  10.                         perror("cannot create communication socket");
  11.                         return 1;
  12.         }      
  13.         memset(&srv_addr, 0, sizeof(srv_addr));
  14.         srv_addr.sin_family = AF_INET;
  15.         srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  16.         srv_addr.sin_port = htons(port);
  17.         ret = connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));

  18.         if(-1 == ret)
  19.         {      
  20.                         perror("cannot connect to the server");
  21.                         printf("[CONNECT_ERROR]%s\n",strerror(errno));
  22.                         return 1;
  23.         }      

  24.         sleep(5);
  25.         printf("try write\n");
  26.         write(connect_fd, "hello server!", 13);
  27.         printf("write orver\n");
  28.         sleep(10);
  29.         return 0;
  30. }
复制代码
问题是:  客户端write操作后, EPOLLIN 、EPOLLOUT 各触发一次.
           但是客户端关闭时, 未将fd从epoll中清除,  这是EPOLLIN、EPOLLOUT 交替触发,陷入死循环.   
           
           没想明白为什么会陷入死循环? 恳请高手指点下,谢谢.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
2 [报告]
发表于 2013-04-07 20:29 |只看该作者
你不检查read=0, 怎么知道何时关闭?

论坛徽章:
0
3 [报告]
发表于 2013-04-07 20:41 |只看该作者
对,如你所说.可以通过read到的大小判断客户端关闭.
我这里暂时不关心这个.
死循环的输出是在客户端退出后出现的.所以我知道是客户端关闭了.

主要关心, 产生死循环的原因.
回复 2# linux_c_py_php


   

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
4 [报告]
发表于 2013-04-07 20:44 |只看该作者
EOF始终可读啊, 当然死循环啊.

论坛徽章:
0
5 [报告]
发表于 2013-04-07 20:53 |只看该作者
谢谢,还有点疑问, 为什么write操作没有陷入死循环? 求指点.回复 4# linux_c_py_php


   

论坛徽章:
0
6 [报告]
发表于 2013-04-07 20:57 |只看该作者
我测试下, 是不是阻塞在read上了.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
7 [报告]
发表于 2013-04-07 20:58 |只看该作者
因为你注册的ET啊, 通知一次就不通知了.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
8 [报告]
发表于 2013-04-07 20:59 |只看该作者
这年头哪有人用ET啊, 用LT吧, ET要讲究一些不好弄.

论坛徽章:
0
9 [报告]
发表于 2013-04-07 20:59 |只看该作者
楼主在ET模式下触发了EPOLLIN事件,但是并没有调用read将数据从缓冲区读出,由于已经监听过一次EPOLLIN,将不会再触发EPOLLIN事件,(ET模式下触发EPOLLIN事件必须将缓冲区的数据全部读完,否则以后将无法再次触发该事件)。但关闭客户端的时候,服务端检测到缓冲区中有数据未读完将再次触发EPOLLIN事件,但是楼主依然没有读出缓存的数据反而更改成EPOLLOUT状态,在EPOLLOUT事件中楼主再次更改成EPOLLIN事件,这样不断反复就变成死循环了

论坛徽章:
0
10 [报告]
发表于 2013-04-07 21:37 |只看该作者
本帖最后由 akwhole 于 2013-04-07 21:46 编辑

首先谢谢楼上两位的帮忙.
linux_c_py_php 提醒了我, 缓冲区可读.

我测试了下.

修改服务端read 一次只读2个字符
read(fd, buff, 2);

服务端依次输出
he
ll
o
se
rv
er
!

然后阻塞在read调用上, 没有继续, 这也是没有陷入死循环的原因.   

总结的话:
1.EPOLLIN、EPOLLOUT  可以通过修改注册事件 进行触发.
  epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
2.这种交替切换事件的方式可以把缓冲区数据全部读完. (采用非阻塞读写,读到的内容长度少于预期时, 认为已经读完, 终止切换)

所贴代码纯粹是为了测试, 尤其是EPOLLIN中的read操作,采用阻塞读, 多个客户端进行请求时,  是有问题的.
  
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP