本文共 1996 字,大约阅读时间需要 6 分钟。
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操作(从而导致它再次变成未就绪),内核就不会发送更多的通知(on
accept问题:
在ET模式socket非阻塞的情况下(上面代码中就是这种情况),多个连接同时到达,服务器的TCP就绪队列瞬间积累多个就绪连接,由于是边缘触发模式,epoll只会通知一次,accept只处理一个连接,导致TCP就绪队列中剩下的连接都得不到处理。解决办法是用while循环accept调用,处理完TCP就绪队列中的所有连接后再退出循环。当accept返回-1并且errno设置为EAGAIN就表示所有连接都处理完。
数据收发问题:
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的状态呢?1,当本地还没调用connect函数,却将套接字送交epoll检测,epoll会产生一次 EPOLLOUT | EPOLLHUP, 也就是产生一个值为0x14的events.
2,当本地connect事件发生了,但建立连接失败,则epoll会产生一次 EPOLLIN | EPOLLERR | EPOLLHUP, 也就是一个值为0x19的events.
3,当connect函数也调用了,而且连接也顺利建立了,则epoll会产生一次 EPOLLOUT, 值为0x4,即表明套接字已经可写。
因而,要判断连接建立,只需要判断该套接字有且仅有可写属性即可。
转载地址:http://oysoi.baihongyu.com/