TCP 并没有像您认为的那样真正起作用,尽管我们可以做一些事情来使其更好地为您工作。但首先让我们更好地了解它是如何工作的,以及为什么你会看到你所做的行为。
当您打开 TCP 连接时,TCP 使用 3 次握手来建立连接。客户端发送一个SYN,服务器响应SYN+ACK,然后客户端发回一个ACK。如果双方都没有尝试发送任何内容,则连接将闲置在那里。您可以从机器上拔下电缆。一棵树可能会倒下并破坏您的互联网服务。互联网提供商可以来维修您的互联网服务,您可以将电缆插回以太网端口。然后客户端可以写入套接字,它应该被传递到服务器。(不幸的是,防火墙故意破坏标准,您的防火墙可能已决定在您等待 ISP 修复您的服务时使连接超时。)但是,如果您在拔下电缆时尝试建立另一个连接,TCP 将尝试发送 SYN,并且很可能会发现存在“没有到主机的路由。” 所以它不能建立一个新的连接。
如果您在 Internet 服务中断时尝试写入套接字,TCP 将尝试发送数据并等待来自服务器的 ACK。在重传超时后,如果它没有收到 ACK,它将再次尝试并在超时时以指数方式回退。通常在 15 次尝试后它会放弃,这通常需要半小时到一个半小时之间的任何时间。
如您所见,TCP 试图在面对故障时保持弹性,而您希望快速了解故障。需要对连接故障做出快速反应的系统(例如通常会在连接故障时取消未结订单的电子证券交易所)通过定期发送心跳消息并在心跳充分过期时采取行动来处理此问题,作为更高级别协议的一部分。
但是,如果您无法控制协议,则可以使用一些套接字选项来改善这种情况。SO_KEEPALIVE 导致 TCP 定期发送 keepalive 数据包,最终会超时,具体取决于 TCP_KEEPIDLE、TCP_KEEPINTVL 和 TCP_KEEPCNT 的设置。TCP_USER_TIMEOUT 允许您设置写入套接字的数据可以保持未确认状态的超时时间。
这两个选项究竟如何工作和交互取决于实现,并且您必须考虑当没有未确认的数据时会发生什么,当有未确认的数据时,以及当有一个缓慢的消费者导致零窗口时会发生什么。通常,建议将它们与设置为 (TCP_KEEPIDLE + TCP_KEEPINTVL*TCP_KEEPCNT) * 1000 的 TCP_USER_TIMEOUT 一起使用以获得一致的结果。
我们的朋友 Cloudflair 有一篇很好的博客文章,介绍了它们究竟是如何协同工作的,但不幸的是在 Linux 上。对于 Windows,我不知道有什么比这更全面的。