epoll边缘触发模式下:epoll_wait()是否报告所有套接字关闭事件?是否需监控读写返回值?
Epoll边缘触发(ET)模式下的套接字关闭事件处理
这绝对是写ET模式网络程序时最容易踩坑的点之一,我来给你掰扯清楚:
1. epoll_wait对关闭事件的通知逻辑
当对方主动发送FIN包(正常关闭连接)时,你的套接字会进入可读状态(因为此时read()会返回0)。在ET模式下,epoll_wait会触发一次EPOLLIN事件,但仅此一次——ET模式的核心是只通知「状态从不可读变为可读」的变化,如果你没及时处理这次事件(比如没调用read()),后续epoll_wait不会再给你发通知,这个连接的关闭状态就会被你漏掉。
但如果是对方异常断开(比如发送RST包),epoll_wait大概率不会触发任何事件,只有当你尝试对这个套接字执行read()或者write()操作时,才会通过错误码得知连接已断。
2. 必须结合read()/write()的返回值判断关闭
不管epoll_wait有没有通知,你都得在处理事件的时候调用read()/write(),并根据返回值做判断:
- 当
read()返回0:说明对方已经正常关闭连接,你这边也应该关闭套接字并从epoll实例中移除。 - 当
read()返回-1,且errno == ECONNRESET:说明对方异常断开连接,同样需要清理资源。 - 当
write()返回-1,且errno == EPIPE或ECONNRESET:说明连接已经断开(可能对方已经关闭,或者异常中断),此时也要处理关闭逻辑。
3. 和注册的epoll标志密切相关
你注册epoll事件时的标志直接影响你能不能收到关闭相关的通知:
- 如果你只注册了
EPOLLIN:对方正常关闭(FIN)时会收到EPOLLIN事件,但异常断开(RST)不会触发事件,只能靠后续read()发现。 - 如果你只注册了
EPOLLOUT:对方正常关闭(FIN)时,epoll_wait不会触发EPOLLOUT事件(除非之前套接字处于不可写状态后变为可写,但关闭和可写状态无关),你只能在调用write()时收到EPIPE错误才知道连接断了。 - 建议同时注册
EPOLLIN | EPOLLOUT,但处理事件时要分别判断是可读还是可写事件,避免误处理。
总结
epoll_wait不会报告所有的套接字关闭事件,尤其是异常断开的情况。你必须在处理epoll事件时,主动调用read()/write()并检查返回值,才能完整覆盖所有连接关闭的场景,而且注册的epoll标志会影响你能否收到正常关闭的通知,这点一定要注意。
内容的提问来源于stack exchange,提问作者porente
相关产品推荐
相关产品推荐

