TCP 套接字连接是否具有“保持活动状态”?

新手上路,请多包涵

我听说过 HTTP keep-alive 但现在我想打开一个与远程服务器的套接字连接。

现在这个套接字连接会永远保持打开状态,还是有一个与它相关的超时限制,类似于 HTTP keep-alive?

原文由 Kevin Boyd 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 791
2 个回答

TCP 套接字在关闭之前保持打开状态。

也就是说,如果不实际发送数据,很难检测到断开的连接(断开,如路由器死机等,而不是关闭),因此大多数应用程序每隔一段时间就会进行某种乒乓反应,以确保连接实际上仍然存在。

原文由 Matthew Scharley 发布,翻译遵循 CC BY-SA 2.5 许可协议

TCP 套接字连接是否具有“保持活动状态”?

简短的回答是 的,有一个通过 TCP Keep-Alive 强制执行的超时,所以套接字不会永远保持打开状态,但可能会在几个 小时 后超时。

如果您想在您的机器上配置 Keep-Alive 超时,请参阅下面的“更改 TCP 超时”部分。否则通读答案的其余部分以了解 TCP Keep-Alive 的工作原理。

介绍

TCP 连接由两个套接字组成,连接的每一端各有一个。当一方想要终止连接时,它会发送一个 FIN 另一方确认并关闭其套接字的数据包。

然而,在此之前,双方都将无限期地保持他们的套接字打开。这留下了一种可能性,即一方可能有意或由于某些错误而关闭其套接字,而不通过 FIN 通知另一端。为了检测这种情况并关闭陈旧的连接,使用了 TCP Keep Alive 过程。

保活过程

三个可配置的属性决定了 Keep-Alives 的工作方式。在 Linux 上它们是1 :

  • tcp_keepalive_time
    • 默认 7200 秒
  • tcp_keepalive_probes
    • 默认 9
  • tcp_keepalive_intvl
    • 默认 75 秒

这个过程是这样的:

  1. 客户端打开 TCP 连接
  2. 如果连接静默 tcp_keepalive_time 秒,发送一个空的 ACK 数据包。 1个
  3. 服务器是否以其自己的相应 ACK 响应?
      1. 等待 tcp_keepalive_intvl 秒,然后发送另一个 ACK
      2. 重复直到发送的 ACK 探测数量等于 tcp_keepalive_probes
      3. 如果此时未收到响应,则发送 RST 并终止连接。
    • :返回步骤 2

在大多数操作系统上默认启用此过程,因此一旦另一端无响应 2 小时 11 分钟(7200 秒 + 75 * 9 秒),死 TCP 连接就会定期修剪。

陷阱

2 小时默认值

由于该过程在默认情况下连接闲置两个小时后才会开始,因此陈旧的 TCP 连接在被修剪之前可能会持续很长时间。这对于昂贵的连接(例如数据库连接)尤其有害。

Keep-Alive 是可选的

根据 RFC 1122 4.2.3.6 ,响应和/或中继 TCP Keep-Alive 数据包 _是可选的_:

实现者可以在他们的 TCP 实现中包含“keep-alives”,尽管这种做法没有被普遍接受。如果包含保持活动,应用程序必须能够为每个 TCP 连接打开或关闭它们,并且它们必须默认为关闭。

非常重要的是要记住,不包含数据的 ACK 段不能通过 TCP 可靠地传输。

原因是 Keep-Alive 数据包不包含数据,也不是绝对必要的,如果过度使用,可能会堵塞互联网的管道。

然而, _在实践中_,我的经验是随着带宽变得更便宜,这种担忧会随着时间的推移而减少。因此 Keep-Alive 数据包通常不会被丢弃。例如, Amazon EC2 文档 间接认可了 Keep-Alive,因此如果您使用 AWS 托管,您可能会安全地依赖 Keep-Alive,但您的里程可能会有所不同。

更改 TCP 超时

每个插槽

2022 年更新: 显然, 从 Java 11 开始,您可以在 Java TCP 套接字本身上设置这些。

不幸的是,由于 TCP 连接是在操作系统级别管理的,旧版本的 Java 不支持在每个套接字级别配置超时,例如 java.net.Socket 。我发现了一些尝试3使用 Java 本机接口 (JNI) 来创建调用本机代码来配置这些选项的 Java 套接字,但似乎都没有得到广泛的社区采用或支持。

相反,您可能被迫将您的配置作为一个整体应用于操作系统。请注意,此配置将影响整个系统上运行的所有 TCP 连接。

Linux

当前配置的 TCP Keep-Alive 设置可以在

  • /proc/sys/net/ipv4/tcp_keepalive_time
  • /proc/sys/net/ipv4/tcp_keepalive_probes
  • /proc/sys/net/ipv4/tcp_keepalive_intvl

您可以像这样更新其中任何一个:

 # Send first Keep-Alive packet when a TCP socket has been idle for 3 minutes
$ echo 180 > /proc/sys/net/ipv4/tcp_keepalive_time
# Send three Keep-Alive probes...
$ echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
# ... spaced 10 seconds apart.
$ echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl

此类更改不会通过重新启动而持续存在。要进行持久更改,请使用 sysctl

 sysctl -w net.ipv4.tcp_keepalive_time=180 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_intvl=10

苹果操作系统

可以使用 sysctl 查看当前配置的设置:

 $ sysctl net.inet.tcp | grep -E "keepidle|keepintvl|keepcnt"
net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.keepcnt: 8

值得注意的是,Mac OS X 以毫秒为单位定义 keepidlekeepintvl ,而 Linux 使用秒。

可以使用 sysctl 设置属性,这将在重新启动后保留这些设置:

 sysctl -w net.inet.tcp.keepidle=180000 net.inet.tcp.keepcnt=3 net.inet.tcp.keepintvl=10000

或者,您可以将它们添加到 /etc/sysctl.conf (如果文件不存在则创建该文件)。

 $ cat /etc/sysctl.conf
net.inet.tcp.keepidle=180000
net.inet.tcp.keepintvl=10000
net.inet.tcp.keepcnt=3

视窗

我没有要确认的 Windows 机器,但您应该在注册表中找到相应的 TCP Keep-Alive 设置,网址为

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TCPIP\Parameters

脚注

1. 有关详细信息,请参阅 man tcp

2. 这个数据包通常被称为“Keep-Alive”数据包,但在 TCP 规范中它只是一个常规的 ACK 数据包。像 Wireshark 这样的应用程序能够通过元分析它包含的序列号和确认号来将其标记为“Keep-Alive”数据包,并参考套接字上的先前通信。

3. 我从基本的 Google 搜索中找到的一些示例是 lucwilliams/JavaLinuxNetflonatel/libdontdie

原文由 Cory Klein 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题