TCP套接字能注意到网络中断的异常吗?

Can a TCP socket notice the exception of network broken?

本文关键字:异常 中断 网络 套接字 注意到 TCP      更新时间:2023-10-16

我在linux上通过套接字与服务器建立了tcp链接。我使用select()函数来监视是否有数据,如果有,我使用recv来获取数据。

现在我想知道网络是否坏了(比如电缆被拔掉了)但是,即使我监视异常,我也无法得到异常。

FD_SET( m_socket, &except_fds );
int result = select( m_socket + 1, &fds, 0, &except_fds, timeout == -1 ? 0 : &tv );

让我困惑的是android上有类似的实现(java.net.socket),如果我将手机设置为飞行模式,我可以立即捕获异常。

select()实现平台是特定的吗?

总之,这种方法是否可以用来监控网络的破碎?如果没有,有什么解决办法吗?

TCP协议的一般工作方式,一个错误是这样的数据传输失败(不是ACK 'd)。除非数据传输失败,否则没有错误。

因此,您可以定期发送小数据包来检测使用setsockopt SO_KEEPALIVE的断开连接,或者定义一个简单的心跳协议。您也可以使用TCP_KEEPCNT, TCP_KEEPIDLETCP_KEEPINTVL覆盖keepalive默认值

让我困惑的是android上有类似的实现(java.net.Socket),如果我将手机设置为飞行模式,我可以立即捕获异常。

不太相似。java.net.Socket不使用select(),除非在不支持SO_RCVTIMEO的平台上读取超时。

select()实现平台是否特定?

当然。

总之,这种方法是否可以用来监控网络的破碎?

如果没有,有什么解决办法吗?

唯一可以可靠地检测到TCP连接断开的方法是尝试写入它。最终,在考虑缓冲和重试之后,write()send()和朋友将返回-1与errno == ECONNRESET

您不需要做什么特别的事情。TCP连接的丢失,无论是由于另一端关闭它还是由于错误,都不是例外。您已经在等待套接字可读,如果连接关闭或出现错误,您的等待就结束了。当等待结束时,您的代码应该已经尝试从套接字读取,因此它应该已经检测到这种情况。

请注意,在大多数平台上,暂时失去连接不会关闭TCP连接。在临时连接丢失的情况下,这将是非常恼人的。事实上,在过去,有些系统具有长时间的TCP连接,但只有在有活动时才具有网络连接。即使故意禁用网络连接,这些连接仍保持活动状态。这个行为是设计好的。

检测TCP链路是否仍然可用的最可靠方法是在其上发送数据。如果链路的另一端无法确认数据,则发送最终将超时。(对send函数的调用可能已经返回成功,但超时将触发一个错误,使套接字可读。

如果你想检测网络条件的变化,你将需要使用特定的系统服务。

在较低的级别,您可以在udev中使用规则检测网络设备的热插拔或移除。更高一些的是像NetworkManager这样的服务,它将通过DBUS进行通信。您可以订阅它以获得网络更改的通知。

如果你没有使用NetworkManager,那么它取决于你的系统脚本。有些有ifdown-post和ifdown-local。其他一些脚本可以运行以响应DHCP事件,其中包括网络拔掉。其他一些程序可以运行来监视网络插头状态,如ifplug或ifplugd或netplugd。

如果你想让内核直接通知你,而不是使用系统服务或守护进程,我认为你需要使用netlink协议来扫描可用的网络设备。

您可能需要将套接字设置为使用keep alive,以便它能够检测连接是否已断开。您需要使用setsocketopt()SO_KEEPALIVE作为第三个参数。

如果select正在监视的任何文件描述符报告错误,则select将返回。

对于套接字,如果操作系统与本地链路层网络失去连接,将报告错误。这意味着如果主机完全失去网络连接,select将返回。当你拔掉网线或手机切换到飞行模式时,就会发生这种情况。

然而,TCP没有办法知道远程主机是否可达,所以如果远程路由器坏了或远程主机消失了。您不太可能收到一个错误,特别是当您没有主动与远程主机通信时,因此,直到您的连接超时,您将不知道任何问题。