94

我听说过 HTTP keep-alive,但现在我想打开与远程服务器的套接字连接。
现在这个套接字连接会永远保持打开状态,还是有一个与它相关的超时限制,类似于 HTTP keep-alive?

4

8 回答 8

137

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

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

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

介绍

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

但是,在此之前,双方将无限期地保持其套接字打开。这留下了一侧可能有意或由于某些错误而关闭其套接字的可能性,而无需通过RST. 为了检测这种情况并关闭陈旧的连接,使用了 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”,尽管这种做法并未被普遍接受。如果包含 keep-alives,应用程序必须能够为每个 TCP 连接打开或关闭它们,并且它们必须默认为关闭。

...

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

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

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

更改 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

Mac OS X

当前配置的设置可以通过以下方式查看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和,而 Linux 使用秒。keepintvl

可以设置属性,这些属性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 这样的应用程序能够通过对它包含的序列和确认号的元分析来将其标记为“保持活动”数据包,并参考套接字上的先前通信。

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

于 2015-11-25T22:21:28.310 回答
76

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

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

于 2009-09-26T02:14:34.543 回答
57

您正在寻找 SO_KEEPALIVE 套接字选项。

Java Socket API通过setKeepAlivegetKeepAlive方法向应用程序公开“保持活动” 。

编辑: SO_KEEPALIVE 在操作系统网络协议栈中实现,而不发送任何“真实”数据。保持活动间隔取决于操作系统,并且可以通过内核参数进行调整。

由于没有发送数据,所以 SO_KEEPALIVE 只能测试网络连接的活跃度,而不是套接字连接的服务的活跃度。要测试后者,您需要实现一些涉及向服务器发送消息并获得响应的东西。

于 2009-09-26T02:24:27.630 回答
36

TCP keepalive 和 HTTP keepalive 是非常不同的概念。在 TCP 中,keepalive 是发送来检测陈旧连接的管理数据包。在 HTTP 中,keepalive 表示持久连接状态。

这是来自 TCP 规范,

只有在一个时间间隔内没有收到连接的数据或确认数据包时,才必须发送保持活动数据包。此间隔必须是可配置的,并且必须默认为不少于两个小时。

如您所见,默认的 TCP keepalive 间隔对于大多数应用程序来说太长了。您可能必须在应用程序协议中添加 keepalive。

于 2009-09-26T02:52:51.373 回答
25

如果您使用伪装的 NAT(就像现在大多数家庭用户一样),则外部端口池有限,并且必须在 TCP 连接之间共享这些端口。因此,如果在特定时间段内没有发送任何数据,伪装的 NAT 往往会假定连接已终止。

这个问题和其他此类问题(两个端点之间的任何地方)可能意味着如果您在合理的空闲期后尝试发送数据,则连接将不再“工作”。但是,在您尝试发送数据之前,您可能不会发现这一点。

使用 keepalives 既可以减少连接被中断的机会,也可以让您更快地发现连接中断的情况。

于 2009-09-26T02:32:49.203 回答
4

这是一些关于keepalive的补充文献,它更详细地解释了它。

http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO

由于 Java 不允许您控制实际的 keepalive 时间,因此如果您使用的是 Linux 内核(或基于 proc 的操作系统),则可以使用示例来更改它们。

于 2012-04-17T21:26:09.527 回答
1

在 JAVA Socket – TCP 连接在操作系统级别进行管理,java.net.Socket 不提供任何内置函数来为每个套接字级别的 keepalive 数据包设置超时。但是我们可以为 java socket 启用 keepalive 选项,但默认情况下需要 2 小时 11 分钟(7200 秒)来处理陈旧的 tcp 连接。在清除之前,此原因连接将可用很长时间。所以我们找到了一些解决方案来使用 Java 原生接口 (JNI),它调用原生代码 (c++) 来配置这些选项。

****视窗操作系统****

在 Windows 操作系统中,keepalive_time & keepalive_intvl 可以配置,但 tcp_keepalive_probes 不能更改。默认情况下,初始化 TCP 套接字时,将 keep-alive 超时设置为 2 小时,将 keep-alive 间隔设置为 1 秒。保持活动超时的默认系统范围值可通过 KeepAliveTime 注册表设置进行控制,该设置以毫秒为单位。

在 Windows Vista 及更高版本中,keep-alive 探测(数据重新传输)的数量设置为 10,并且无法更改。

在 Windows Server 2003、Windows XP 和 Windows 2000 上,keep-alive probe 的数量默认设置为 5。keep-alive probe 的数量是可控的。对于 windows Winsock IOCTLs 库用于配置 tcp-keepalive 参数。

int WSAIoctl( SocketFD, // 识别套接字的描述符 SIO_KEEPALIVE_VALS, // dwIoControlCode (LPVOID) lpvInBuffer, // 指向 tcp_keepalive struct (DWORD) cbInBuffer, // 输入缓冲区长度 NULL, // 输出缓冲区 0, // 大小输出缓冲区 (LPDWORD) lpcbBytesReturned, // 返回的字节数 NULL, // OVERLAPPED 结构 NULL // 完成例程 );

操作系统

Linux 内置了对 keepalive 的支持,需要启用 TCP/IP 网络才能使用它。程序必须使用 setsockopt 接口为其套接字请求保持活动控制。

int setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)

每个客户端套接字都将使用 java.net.Socket 创建。每个套接字的文件描述符 ID 将使用 java 反射检索。

于 2018-11-26T05:25:29.123 回答
0

根据Microsoft 文档,适用于 Windows

  • KeepAliveTime(REG_DWORD,毫秒,默认情况下未设置,这意味着 7,200,000,000 = 2 小时) - 类似于 tcp_keepalive_time
  • KeepAliveInterval(REG_DWORD,毫秒,默认情况下未设置,这意味着 1,000 = 1 秒) - 类似于 tcp_keepalive_intvl
  • 由于 Windows Vista 没有类似 tcp_keepalive_probes,值固定为 10 并且不能更改
于 2018-12-07T09:33:06.013 回答