天天看点

linux下epoll模型并发问题

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

linux下epoll模型并发问题

<img class="alignnone size-full wp-image-16" alt="t_1146193487" src="http://www.bo56.com/wp-content/uploads/2013/07/t_1146193487.jpg" width="500" height="76" /></a></p>

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

注意倒数第二行: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就表示所有连接都处理完。

修改后的代码如下:

同理,接收数据和发送数据时如果是et模式,且非阻塞,也得用循环。

读:只要可读,就一直读,直到返回0,或者 errno = eagain

写:只要可写,就一直写,直到数据发送完,或者 errno = eagain

正确的读:

正确的写:

继续阅读