我听说过 HTTP keep-alive 但现在我想打开一个与远程服务器的套接字连接。
现在这个套接字连接会永远保持打开状态,还是有一个与它相关的超时限制,类似于 HTTP keep-alive?
原文由 Kevin Boyd 发布,翻译遵循 CC BY-SA 4.0 许可协议
我听说过 HTTP keep-alive 但现在我想打开一个与远程服务器的套接字连接。
现在这个套接字连接会永远保持打开状态,还是有一个与它相关的超时限制,类似于 HTTP keep-alive?
原文由 Kevin Boyd 发布,翻译遵循 CC BY-SA 4.0 许可协议
TCP 套接字连接是否具有“保持活动状态”?
简短的回答是 是 的,有一个通过 TCP Keep-Alive 强制执行的超时,所以套接字不会永远保持打开状态,但可能会在几个 小时 后超时。
如果您想在您的机器上配置 Keep-Alive 超时,请参阅下面的“更改 TCP 超时”部分。否则通读答案的其余部分以了解 TCP Keep-Alive 的工作原理。
TCP 连接由两个套接字组成,连接的每一端各有一个。当一方想要终止连接时,它会发送一个 FIN
另一方确认并关闭其套接字的数据包。
然而,在此之前,双方都将无限期地保持他们的套接字打开。这留下了一种可能性,即一方可能有意或由于某些错误而关闭其套接字,而不通过 FIN
通知另一端。为了检测这种情况并关闭陈旧的连接,使用了 TCP Keep Alive 过程。
三个可配置的属性决定了 Keep-Alives 的工作方式。在 Linux 上它们是1 :
tcp_keepalive_time
tcp_keepalive_probes
tcp_keepalive_intvl
这个过程是这样的:
tcp_keepalive_time
秒,发送一个空的 ACK
数据包。 1个ACK
响应?
tcp_keepalive_intvl
秒,然后发送另一个 ACK
ACK
探测数量等于 tcp_keepalive_probes
。RST
并终止连接。在大多数操作系统上默认启用此过程,因此一旦另一端无响应 2 小时 11 分钟(7200 秒 + 75 * 9 秒),死 TCP 连接就会定期修剪。
由于该过程在默认情况下连接闲置两个小时后才会开始,因此陈旧的 TCP 连接在被修剪之前可能会持续很长时间。这对于昂贵的连接(例如数据库连接)尤其有害。
根据 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,但您的里程可能会有所不同。
2022 年更新: 显然, 从 Java 11 开始,您可以在 Java TCP 套接字本身上设置这些。
不幸的是,由于 TCP 连接是在操作系统级别管理的,旧版本的 Java 不支持在每个套接字级别配置超时,例如 java.net.Socket
。我发现了一些尝试3使用 Java 本机接口 (JNI) 来创建调用本机代码来配置这些选项的 Java 套接字,但似乎都没有得到广泛的社区采用或支持。
相反,您可能被迫将您的配置作为一个整体应用于操作系统。请注意,此配置将影响整个系统上运行的所有 TCP 连接。
当前配置的 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 以毫秒为单位定义 keepidle
和 keepintvl
,而 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/JavaLinuxNet 和 flonatel/libdontdie 。
原文由 Cory Klein 发布,翻译遵循 CC BY-SA 4.0 许可协议
15 回答8.4k 阅读
7 回答3.2k 阅读✓ 已解决
8 回答6.2k 阅读
3 回答2.3k 阅读✓ 已解决
1 回答4.1k 阅读✓ 已解决
3 回答2.2k 阅读✓ 已解决
2 回答3.1k 阅读
TCP 套接字在关闭之前保持打开状态。
也就是说,如果不实际发送数据,很难检测到断开的连接(断开,如路由器死机等,而不是关闭),因此大多数应用程序每隔一段时间就会进行某种乒乓反应,以确保连接实际上仍然存在。