TCP Keepalive 机制

什么是TCP keepalive?

keepalive的概念非常简单:当建立一个TCP连接时,将关联一组计时器。其中一些计时器处理keepalive过程。当keepalive定时器达到0时,你发送给对方一个keepalive探测包,里面没有数据,并且ACK标志打开。可以这样做是因为TCP/IP规范,作为一种重复的ACK,而且远程端点将没有参数,因为TCP是面向流的协议。另一方面,将收到来自远程主机的回复(它根本不需要支持keepalive,只需要TCP/IP),没有数据和ACK设置。

如果收到keepalive探测的应答,则可以断言连接仍在运行,而不必担心用户级实现。事实上,TCP允许您处理流,而不是包,因此零长度的数据包对用户程序没有危险。

这个过程很有用,因为如果其他对等体失去了连接(例如重新启动),您将注意到连接已经断开,即使您没有流量在连接上。如果keepalive探测没有得到对等体的回应,您可以断言该连接不能被认为是有效的,然后采取正确的操作。

为什么要用TCP keepalive

没有keepalive,你也可以很开心的玩耍,所以如果您正在阅读这篇文章,您可能正在尝试理解keepalive是否可以解决你的问题。

Keepalive是非侵入性的,而且在大多数情况下,如果你有疑问,你可以打开它而不会有出错的风险。但请记住,它会产生额外的网络流量,这可能会对路由器和防火墙产生影响。

  • 检查对端是否死亡
  • 防止由于网络不活跃而断开连接

检查对端是否死亡

当你的对端死亡时,Keepalive可以在它通知你之前通知你。发生这种情况的原因有很多,比如内核panic或处理该对等进程的被终止。当需要keepalive来检测对等体死亡时,另一个场景是当对等体仍然活着,但它与您之间的网络通道发生故障时。在这种情况下,如果网络不能再次运行,则相当于对等死亡。这是一种正常TCP操作对检查连接状态不起作用的情况。

认为一个简单的点和点之间的TCP连接B:最初的三方握手,与一个SYN段从A到B, B SYN / ACK回来一个,最后一个ACK从A到B。在这个时候,我们在一个稳定的状态:建立连接,现在我们通常会等待别人发送数据的通道。问题就来了:拔掉B的电源,它马上就会断开,而不会通过网络发送任何东西来通知A连接即将断开。从A的角度来看,它已经准备好接收数据,并且不知道B已经崩溃。现在将电源恢复到B,等待系统重启。A和B现在又回来了,但是A知道与B的连接仍然处于活动状态,B不知道。当A试图在死连接上向B发送数据,而B回复一个RST包,导致A最终关闭连接时,这种情况就会自行解决。

Keepalive可以告诉你何时对端变得不可达,而不会有误报的风险。事实上,如果问题出现在两个对等体之间的网络中,那么keepalive操作就是等待一段时间,然后重试,在标记连接断开之前发送keepalive包。

_____                                                     _____
   |     |                                                   |     |
   |  A  |                                                   |  B  |
   |_____|                                                   |_____|
      ^                                                         ^
      |--->--->--->-------------- SYN -------------->--->--->---|
      |---<---<---<------------ SYN/ACK ------------<---<---<---|
      |--->--->--->-------------- ACK -------------->--->--->---|
      |                                                         |
      |                                       system crash ---> X
      |
      |                                     system restart ---> ^
      |                                                         |
      |--->--->--->-------------- PSH -------------->--->--->---|
      |---<---<---<-------------- RST --------------<---<---<---|
      |                                                         |

防止由于网络不活跃而断开连接

keepalive的另一个有用的目标是防止非活动断开通道。这是一个非常常见的问题,当您在NAT代理或防火墙之后,无缘无故断开连接。此行为是由代理和防火墙中实现的连接跟踪过程引起的,这些过程跟踪通过它们的所有连接。由于这些机器的物理限制,它们只能在内存中保留有限数量的连接。最常见、最符合逻辑的策略是保留最新的连接,首先丢弃旧的、不活跃的连接。

_____           _____                                     _____
   |     |         |     |                                   |     |
   |  A  |         | NAT |                                   |  B  |
   |_____|         |_____|                                   |_____|
      ^               ^                                         ^
      |--->--->--->---|----------- SYN ------------->--->--->---|
      |---<---<---<---|--------- SYN/ACK -----------<---<---<---|
      |--->--->--->---|----------- ACK ------------->--->--->---|
      |               |                                         |
      |               | <--- connection deleted from table      |
      |               |                                         |
      |--->- PSH ->---| <--- invalid connection                 |
      |               |                                         |