linux下epoll模型accept并发问题

最近用c++实现了贝叶斯分类算法,做了个自动识别垃圾信息的小工具。工具中有个功能,通过绑定指定端口,和客户端通信。服务端使用的是epoll网络模型。在测试的时候发现,单用户的情况下客户端和服务器通信正常。但是在多用户并发的情况下,客户端和服务端通信不正常。此时,客户端能正常的链接,发送数据,但是一直卡在接收数据部分。如下图:

出现这种问题,是因为不正确的使用了epoll中的ET(edge-trigger)模式。代码如下:

01/**************************************************
02函数名:acceptConn
03功能:接受客户端的链接
04参数:srvfd:监听SOCKET
05***************************************************/
06void acceptConn(int srvfd)
07{
08struct sockaddr_in sin;
09socklen_t len = sizeof(struct sockaddr_in);
10bzero(&sin, len);
11 
12int confd = accept(srvfd, (struct sockaddr*)&sin, &len);
13 
14if (confd < 0)
15{
16printf("%s: bad accept\n");
17return;
18}else
19{
20printf("Accept Connection: %d", confd);
21}
22 
23setNonblocking(confd);
24 
25//将新建立的连接添加到EPOLL的监听中
26struct epoll_event event;
27event.data.fd = confd;
28event.events =  EPOLLIN|EPOLLET;
29epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event);
30}

注意倒数第二行:event.events = EPOLLIN|EPOLLET; 采用的是ET模式。下面我们来具体说下,问题出在那里。

在epoll中有两种模式:level-trigger模式,简称LT模式,和edge-trigger模式,简称ET模式。其中,LT是默认的工作模式。

这两种模式的工作方式有些不同。在level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

在ET模式socket非阻塞的情况下(上面代码中就是这种情况),多个连接同时到达,服务器的TCP就绪队列瞬间积累多个就绪连接,由于是边缘触发模式,epoll只会通知一次,accept只处理一个连接,导致TCP就绪队列中剩下的连接都得不到处理。因此,就出现了上面所提及的问题。

解决办法是用while循环抱住accept调用,处理完TCP就绪队列中的所有连接后再退出循环。如何知道是否处理完就绪队列中的所有连接呢?accept返回-1并且errno设置为EAGAIN就表示所有连接都处理完。

修改后的代码如下:

01/**************************************************
02函数名:acceptConn
03功能:接受客户端的链接
04参数:srvfd:监听SOCKET
05***************************************************/
06void acceptConn(int srvfd)
07{
08struct sockaddr_in sin;
09socklen_t len = sizeof(struct sockaddr_in);
10bzero(&sin, len);
11int confd = 0;
12while((confd = accept(srvfd, (struct sockaddr*)&sin, &len)) > 0) {
13
14printf("Accept Connection: %d", confd);
15 
16setNonblocking(confd);
17 
18//将新建立的连接添加到EPOLL的监听中
19struct epoll_event event;
20event.data.fd = confd;
21event.events =  EPOLLIN|EPOLLET;
22epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event);
23}
24if (confd == -1) {
25if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR){
26printf("%s: bad accept\n");
27return;
28}
29}
30}
同理,接收数据和发送数据时如果是ET模式,且非阻塞,也得用循环。
读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN
写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN
正确的读:
1n = 0;
2while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
3n += nread;
4}
5if (nread == -1 && errno != EAGAIN) {
6perror("read error");
7}

正确的写:

01int nwrite, data_size = strlen(buf);
02n = data_size;
03while (n > 0) {
04nwrite = write(fd, buf + data_size - n, n);
05if (nwrite < n) {
06if (nwrite == -1 && errno != EAGAIN) {
07perror("write error");
08}
09break;
10}
11n -= nwrite;
12}

http://www.searchtb.com/2013/07/linux-epoll-accept.html

【6层】一字型框架办公楼(含建筑结构图、计算书) 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值