epoll的一些关键点和总结(一)

来源:互联网 发布:德乌洛费乌数据 编辑:程序博客网 时间:2024/05/18 09:12

1.两种工作模式以及ET模式下的数据收发异常:

在epoll中有两种模式:默认的level-trigger模式(水平触发),简称LT模式,和edge-trigger模式(边缘触发),简称ET模式。

LT同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表
ET是高速工作方式(但不一定比LT高,TCP中需更多benchmark确认),只支持no-block socket。 在这种模式下,当描述符从未就绪变为就绪时,内核就通过epoll告诉你,然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的 就绪通知,直到你做了某些操作而导致那个文件描述符不再是就绪状态(比如 你在发送,接收或是接受请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK即EAGAIN 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核就不会发送更多的通知(only once)。

accept问题:

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

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

数据收发问题:

读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN
写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN, 若EAGAIN且数据未发完,这时貌似有两种处理方案:
1.continue循环继续写,直到缓冲区非空把数据都写完,但是貌似暂用较多时间;
2.break出循环,记录写了多少,还有多少没写,下次从那里继续写,因为EAGAIN是由于缓冲区满,因此当非满时仍会触发OUT事件。

2.关于非阻塞connect:
TCP中connect函数会激发TCP的三次握手过程,而三次握手是需要一些时间的,内核中对connect的超时限制是75秒,就是说如果超过75秒则connect会由于超时而返回失败。但是如果对端服务器由于某些问题无法连接,那么每一个客户端发起的connect都会要等待75才会返回,因为socket默认是阻塞的。这种情况对某些服务来说是致命的,因此很有必要使用非阻塞socket进行connect。
将socket设置成非阻塞:
void CSocket::SetNonblocking(int fd) const{int flags;if ((flags = fcntl(fd, F_GETFL)) < 0){perror("fcntl(Getfl)");exit(-1);}flags = flags | O_NONBLOCK;if (fcntl(fd, F_SETFL, flags) < 0){perror("fcntl(set Nonblocking)");exit(-1);}}

当我们将socket设置为NONBLOCK后,在调用connect的时候,如果操作不能马上完成,那connect便会立即返回,

如果connect返回-1且errno=EINPROGRESS时,这说明在继续进行,但是仍未完成;同时TCP的三路握手操作继续进行;后续只要用select/epoll去注册对应的事件并设置超时时间来判断连接否是连接成功就可以了。

3.ET模式下用epoll判断非阻塞connect连接状态:

如上所述,如何判断connect的状态呢?
因为epoll本身没有明确提出当异步connect成功之后会返回什么样的信号,通过测试有如下结果:

1,当本地还没调用connect函数,却将套接字送交epoll检测,epoll会产生一次 EPOLLOUT | EPOLLHUP, 也就是产生一个值为0x14的events.


2,当本地connect事件发生了,但建立连接失败,则epoll会产生一次 EPOLLIN | EPOLLERR | EPOLLHUP, 也就是一个值为0x19的events.


3,当connect函数也调用了,而且连接也顺利建立了,则epoll会产生一次 EPOLLOUT, 值为0x4,即表明套接字已经可写。


因而,要判断连接建立,只需要判断该套接字且仅有可写属性即可。



0 0