EPOLL下的accept(转载)

(转载者注:看完这个,再回头看看nginx源码,发现它在accept时用的是LT模式,read,write时是ET模式)
不知道是谁第一个犯了错,在网上贴出所谓epoll通用框架的代码。注意看accpet的处理:

1 epfd = epoll_create(10);
2  
3 struct sockaddr_in clientaddr;
4 struct sockaddr_in serveraddr;
5 listenfd = socket(AF_INET, SOCK_STREAM, 0);
6  
7 bool bReuseaddr = 1;
8 //setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(bool));
9 setnonblocking(listenfd);
10 ev.data.fd = listenfd;
11 ev.events = EPOLLIN | EPOLLET;
12 // ev.events=EPOLLIN;
13 epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);
14 bzero(&serveraddr, sizeof(serveraddr));
15 serveraddr.sin_family = AF_INET;
16 // char *local_addr=INADDR_ANY;
17 // inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);
18 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
19 serveraddr.sin_port = htons(SERV_PORT);
20 bind(listenfd, (sockaddr *) &serveraddr, sizeof(serveraddr));
21 listen(listenfd, LISTENQ);
22 maxi = 0;
23 int nfds_count = 0, recvcount = 0, recvlen = 0;
24 for (;;) {
25     cout << "before epoll_wait! nfds_count=" << nfds_count << endl;
26     nfds = epoll_wait(epfd, events, 20, 10000000);
27  
28     // if(nfds>1) cout<<"nfds="<<nfds<<endl;
29     cout << "nfds=" << nfds << endl;
30  
31     for (i = 0; i < nfds; ++i) {
32         if (events[i].data.fd == listenfd) {
33             connfd = accept(listenfd, (sockaddr *) &clientaddr, &clilen);
34             nfds_count += 1;
35             if (connfd < 0) {
36                 perror("connfd<0");
37                 exit(1);
38             }
39             setnonblocking(connfd);
40             char *str = inet_ntoa(clientaddr.sin_addr);
41             cout << "connect from " << str << "connfd=" << connfd << endl;
42             ev.data.fd = connfd;
43             ev.events = EPOLLIN | EPOLLET;
44             //ev.events=EPOLLIN;
45             //ev.events=EPOLLIN|EPOLLOUT|EPOLLET;
46             epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
47  
48         }
49     }
50 }


代码是从某处(很多地方都是这段,连注释都一样)拷过来的。熟悉epoll的人看了应该很熟系,其实就是将linsten的fd也加入到epoll中,当有新连接加入时可以epoll_wait到,随后再用accpet处理。
事实上这段代码是有问题的——高并发的情况下,accept到的fd的数量跟client端的发起请求的数量并不相等。我测了一下,100个并发(其实也不算高了)往往少几个到几十个不等。

相信很多人被这段代码误导了,因为我在遇到问题的时候搜到不少帖子,用的都是这样的代码。只是奇怪的是没见几个人说遇到我提到的这个问题。不过有人说这段代码效率不高,提出用阻塞式IO把accpet提到一个单独的线程里做。撇开这样做性能上的优劣不说,倒是能解决我遇到的问题,但这治标不治本——我要用epoll+nonblockio

下面说说这段误人子弟的代码,其实 man epoll 就能找到这段代码的出处。相信它是某位同志从里面帖出来然后玩弄了许久后好心放到网上的,不然不会有这么多的//注释。而问题就是出在注释上,注意到 man-page 里的代码是这样的:

1 ev.events = EPOLLIN;
2 ev.data.fd = listen_sock;
3 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
4     perror("epoll_ctl: listen_sock");
5     exit(EXIT_FAILURE);
6 }

没错,man里并没有将listenfd以ET的方式加入epoll(不指定EPOLLET默认就是Level Triggered),事实也证明不设ET就没有问题了。

难道说ET模式下就不能以非阻塞模式来accpet?答案是否定的。其实只要把accpet方式改一下就可以了,简单说就是if改while:

1 while ((confd = accept(listenfd, (struct sockaddr*) sa, &clientlen)) > 0) {
2  
3     //add connection to epoll
4     peer = (struct sockaddr*) sa;
5     printf("%s:%d connect at %d.\n", inet_ntoa(peer->sin_addr),
6             peer->sin_port, confd);
7     setnonblocking(confd);
8  
9     ev.data.fd = confd;
10     ev.events = EPOLLIN | EPOLLET ;
11  
12     epoll_ctl(myfd, EPOLL_CTL_ADD, confd, &ev);
13  
14 }

Feedback

# re: EPOLL下的accept(转载)  回复  更多评论   

2011-09-29 18:58 by  dong
嗯,确实是这样的,我刚开始学的时候就发生过这种错,我看过man文档,负责监听 accpet的fd是LT模式的,并不是在网上搜到的ET模式。我之前测过的时候就是遇到客户端量一多的时候,就出现监听到的客户端数量不一的问题,当时查得我那个心烦呀~当时就奇怪别人的代码贴出来之后也没反应有啥问题,但通过在多个版本的LINUX上测试之后,可以肯定就是不用ET模式就没问题。

# re: EPOLL下的accept(转载)  回复  更多评论   

2012-08-24 00:44 by  linxr
呵呵,我今天也找到问题了,才看到你的文章。网上的代码,帮助人也害人哪。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值