一个TCP套接字连接有一个“保持活着”?
我听说过HTTP保持活着,但现在我想打开与远程服务器的套接字连接。
现在这个套接字连接会一直保持打开状态,还是会有类似于HTTP保持连接的超时限制?
TCP套接字保持打开状态直到closures。
也就是说,在没有实际发送数据的情况下,检测到断开的连接(如路由器死亡等,而不是closures)是非常困难的,所以大多数应用程序每隔一段时间就会做一些ping / pong响应,以确保连接实际上仍然活着。
您正在寻找SO_KEEPALIVE套接字选项。
Java Socket API通过setKeepAlive
和getKeepAlive
方法向应用程序公开“keep-alive”。
编辑:SO_KEEPALIVE在操作系统networking协议栈中实现,不发送任何“真实”的数据。 保持活动时间间隔取决于操作系统,可能通过内核参数调整。
由于没有数据发送,SO_KEEPALIVE只能testingnetworking连接的活跃度,而不是套接字连接的服务的活跃度。 为了testing后者,你需要实现一些东西,包括发送消息到服务器并获得响应。
现在这个套接字连接会一直保持打开状态,还是会有类似于HTTP保持连接的超时限制?
简而言之, 是的 ,有一个超时,它通过TCP Keep-Alive强制执行。
如果要configuration保活超时,请参阅下面的“更改TCP超时”部分。
介绍
TCP连接由两个套接字组成,连接的每一端都有一个套接字。 当一方想要终止连接时,它发送另一方确认的RST
分组并closures它们的套接字。
但是,在这之前,双方都将无限期地保持开放。 这就使得一方可能故意地或者由于一些错误而closures其插口,而不通过RST
通知另一端。 为了检测这种情况并closures陈旧的连接,使用TCP Keep Alive进程。
保持活跃的过程
有三个可configuration属性确定Keep-Alives如何工作。 在Linux上,他们是1 :
-
tcp_keepalive_time
- 默认7200秒
-
tcp_keepalive_probes
- 默认9
-
tcp_keepalive_intvl
- 默认75秒
这个过程是这样的:
- 客户端打开TCP连接
- 如果连接静默
tcp_keepalive_time
秒,发送一个空的ACK
数据包。 1 - 服务器是否用自己的相应
ACK
进行响应?- 没有
- 等待
tcp_keepalive_intvl
秒,然后发送另一个ACK
- 重复,直到发送的
ACK
探测的数量等于tcp_keepalive_probes
。 - 如果此时没有收到响应,请发送
RST
并终止连接。
- 等待
- 是 :返回到第2步
- 没有
在大多数操作系统中,默认情况下启用此过程,因此一旦另一端在2小时11分钟(7200秒+ 75 * 9秒)内没有响应,就会定期修剪死的TCP连接。
陷阱
2小时默认
由于默认情况下连接闲置了两个小时才能启动进程,因此在修剪之前,过时的TCP连接可能会持续很长时间。 这对于诸如数据库连接等昂贵的连接可能是特别有害的。
保持活动是可选的
根据RFC 1122 4.2.3.6 ,响应和/或中继TCP Keep-Alive数据包是可选的 :
实现者可以在他们的TCP实现中包含“keep-alive”,尽pipe这种做法并不被普遍接受。 如果包含keepalive,则应用程序必须能够为每个TCP连接打开或closures它们,并且它们必须默认closures。
…
记住不包含数据的ACK段不能被TCP可靠地传输是非常重要的。
其原因是Keep-Alive数据包不包含任何数据,并且不是绝对必要的,如果过度使用,风险会堵塞互联网的pipe道。
然而在实践中 ,我的经验是,随着带宽变得更便宜,这种担心已经减less了。 因此Keep-Alive包通常不会被丢弃。 例如Amazon EC2文档给出了Keep-Alive的间接认可,所以如果您使用AWS托pipe,您可能安全地依赖Keep-Alive,但是您的里程可能会有所不同。
更改TCP超时
每个sockets
不幸的是,由于TCP连接是在操作系统级别进行pipe理的,因此Java不支持在每个套接字级别configuration超时,例如在java.net.Socket
。 我发现一些尝试使用Java本地接口(JNI)来创build调用本地代码来configuration这些选项的Java套接字,但没有一个似乎有广泛的社区采用或支持。
相反,您可能会被迫将您的configuration作为一个整体应用于操作系统。 请注意,此configuration将影响整个系统上运行的所有TCP连接。
Linux的
当前configuration的TCP保持活动设置可以在中find
-
/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
Mac OS X
当前configuration的设置可以用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
(如果该文件不存在,则创build该文件)。
$ cat /etc/sysctl.conf net.inet.tcp.keepidle=180000 net.inet.tcp.keepintvl=10000 net.inet.tcp.keepcnt=3
视窗
我没有Windows机器来确认,但是您应该在registry中find相应的TCP Keep-Alive设置
\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TCPIP\Parameters
有关Windows和TCP超时的更多信息,请参阅此处 。
脚注
1.请参阅man tcp
获取更多信息。
2.这个数据包通常被称为“Keep-Alive”数据包,但是在TCP规范中它只是一个常规的ACK
数据包。 像Wireshark这样的应用程序可以通过对包含在前面的套接字通信中的序列和确认号进行元分析来将其标记为“Keep-Alive”数据包。
3.我从基本的Googlesearch中find的一些例子是lucwilliams / JavaLinuxNet和flonatel / libdontdie 。
TCP keepalive和HTTP keepalive是非常不同的概念。 在TCP中,keepalive是发送用于检测陈旧连接的pipe理数据包。 在HTTP中,keepalive表示持久连接状态。
这是来自TCP规范,
只有在间隔内没有收到连接的数据或确认数据包时,才能发送保持活动的数据包。 这个间隔必须是可configuration的,并且必须默认不less于两个小时。
如您所见,对于大多数应用程序,默认的TCP存活时间间隔太长。 您可能需要在应用程序协议中添加Keepalive。
如果你在伪装NAT(如今大部分家庭用户)背后,有一个有限的外部端口池,这些端口必须在TCP连接之间共享。 因此,如果没有数据在一段时间内被发送,伪装NAT往往假定连接已经被终止。
这个和其他这样的问题(在两个端点之间的任何地方)可能意味着如果您尝试在合理的空闲时间之后发送数据,连接将不再“工作”。 但是,在尝试发送数据之前,您可能不会发现这一点。
使用Keepalive既可以减less连接中断的可能性,也可以让您更快地发现连接断开的情况。
以下是一些有关Keepalive的补充文献,它将以更精细的方式对其进行解释。
http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO
由于Java不允许您控制实际的存活时间,因此如果使用Linux内核(或基于proc的操作系统),则可以使用这些示例来更改它们。