我知道,如果远程主机优雅地关闭连接,epoll
将报告EPOLLIN
,并调用read
或recv
不会阻塞,并将返回 0 字节(即流结束)。
但是,如果连接没有正常关闭,并且write
orsend
操作失败,这是否会导致epoll
随后返回EPOLLIN
该套接字,从而产生相同/相似的流结束场景?
我试图找到有关此行为的文档,但没有成功,虽然我可以对其进行测试,但我对具有特定内核版本的特定发行版上会发生什么不感兴趣。
从规范中确实不是很明显,但它的工作原理如下poll()
:
POLLIN
也会返回。POLLHUP
则POLLERR
返回。shutdown(SHUT_WR)
),POLLIN
则返回并且POLLHUP
不POLLERR
返回。(这允许正常等待POLLOUT
。)简单的做法是在设置和POLLIN
时尝试读取。POLLHUP
POLLERR
在kqueue()
中,只有一个EVFILT_READ
可以触发的过滤器。这在手册页中有描述,应该足够清楚。
请注意,如果您不启用 TCP keepalives(FreeBSD 默认启用它们,但大多数其他操作系统不启用),如果网络以某些方式中断,等待数据读取可能会永远卡住。即使 TCP keepalives 开启,也往往需要几个小时才能检测到断开的连接。
epoll()
基本上是poll()
,但是当你增加 fds 的数量时它可以更好地扩展。我不确定当您将它用作边缘触发接口时它会做什么。但是对于触发级别 - 是的,如果检测到流结束,它将始终返回 EPOLLIN,前提是您正在侦听此事件。
尽管您必须知道TCP并不完美。如果连接被另一端异常终止(物理链路断开),那么在您写入套接字之前,您的一侧可能永远不会检测到这一点。TCP_KEEPALIVE
可能有帮助,但作用不大。
当对端机器意外关闭时,它可能不会返回 EPOLLIN。过去,我通过 VirtualBox 遇到过这种现象,步骤如下:
我看到连接仍然在服务器虚拟机中建立
netstat -anp --tcp
换句话说,EPOLLIN 没有在服务器中触发。
http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/说默认情况下它将保持大约 7200 秒。
当然,您可以通过 setsockopt 或内核参数更改保持活动超时值。
但是有些书说更好的解决方案是在应用层检测它,例如设计确保定期发送一些虚拟消息以检测连接状态的协议。
但是,如果连接没有正常关闭,并且写入或发送操作失败,这是否会导致 epoll 随后为该套接字返回 EPOLLIN,从而产生相同/相似的流结束场景?
不,这意味着收到了一个 FIN,这意味着连接正常终止,但这并没有发生。我希望你会得到一个 EPOLLERR 或者 EPOLLHUP。
但我很好奇为什么你不会在收到写入错误时关闭套接字,以及为什么你仍然会轮询它。那不是正确的行为。