我听说过HTTP keep-alive,但现在我想要和远程服务器建立一个套接字连接。
那么这个套接字连接会一直保持打开状态吗?还是类似于HTTP keep-alive有与之关联的超时限制?
我听说过HTTP keep-alive,但现在我想要和远程服务器建立一个套接字连接。
那么这个套接字连接会一直保持打开状态吗?还是类似于HTTP keep-alive有与之关联的超时限制?
默认情况下,进程要等待两个小时的空闲连接才会启动,因此过期的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 Socket本身上设置这些内容。
不幸的是,由于TCP连接在操作系统级别上进行管理,旧版本的Java不支持在java.net.Socket
等每个套接字级别上配置超时。我发现了一些尝试3使用Java Native Interface(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
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. 我通过基本的谷歌搜索找到了一些示例,如lucwilliams/JavaLinuxNet和flonatel/libdontdie。
TCP套接字一直保持打开状态,直到它们被关闭。
话虽如此,如果不发送数据,很难检测到一个破损的连接(比如路由器出现故障等情况),因此大多数应用程序会定期执行某种ping/pong响应,以确保连接仍然处于活动状态。
setKeepAlive
和getKeepAlive
方法向应用程序公开“保持活动状态”。TCP保活和HTTP保活是非常不同的概念。在TCP中,保活是发送的管理数据包,用于检测失效连接。而在HTTP中,保活意味着持久连接状态。
这来自TCP规范:
仅当连接在一个时间间隔内未收到数据或确认数据包时,才必须发送保活数据包。此时间间隔必须可配置且默认值不得少于2小时。
如你所见,默认的TCP保活间隔对于大多数应用程序来说太长了。您可能需要在应用程序协议中添加保活。
HTTP/1.0
中,每个请求/响应都需要重新连接到服务器。对于HTTP/1.1
,他们引入了一个Keep-Alive
头,可以用来触发服务器在处理完响应后不关闭连接,以便请求更多文件并允许“流水线化”;发送多个请求,然后等待所有数据返回。 - Matthew Scharley这和其他类似问题(在两个端点之间的任何地方)可能意味着如果你在一个合理的空闲期后尝试发送数据,连接将不再“工作”。然而,直到你尝试发送数据之前,你可能还没有发现这一点。
使用keepalives既可以减少连接在中途被中断的机会,也可以让你更早地发现连接出现问题。
以下是一些有关保持连接的补充文献,详细解释了此概念。
http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO
由于Java不能控制实际的保持连接时间,如果您正在使用Linux内核(或基于proc的操作系统),则可以使用示例来更改它们。
int WSAIoctl(
SocketFD, // descriptor identifying a socket
SIO_KEEPALIVE_VALS, // dwIoControlCode
(LPVOID) lpvInBuffer, // pointer to tcp_keepalive struct (DWORD)
cbInBuffer, // length of input buffer
NULL, // output buffer
0, // size of output buffer
(LPDWORD) lpcbBytesReturned, // number of bytes returned
NULL, // OVERLAPPED structure
NULL // completion routine
);
Linux操作系统
Linux内置支持保持连接,需要在启用TCP/IP网络才能使用。程序必须使用setsockopt接口请求其套接字的保持连接控制。
int setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
每个客户端套接字将使用java.net.Socket创建。每个套接字的文件描述符ID将使用Java反射来检索。
针对Windows,根据Microsoft文档进行设置