3. 在 Linux 下使用 TCP keepalive

Linux 内置了对 keepalive 的支持。你需要启用 TCP/IP 网络才能使用它。你还需要procfs支持 和sysctl支持,以便能够在运行时配置内核参数。

涉及 keepalive 的过程使用三个用户驱动的变量

tcp_keepalive_time

上次发送数据包(简单的 ACK 不被视为数据)和第一次 keepalive 探测之间的时间间隔;在连接被标记为需要 keepalive 后,此计数器不再使用

tcp_keepalive_intvl

后续 keepalive 探测之间的时间间隔,无论连接在此期间交换了什么

tcp_keepalive_probes

在认为连接已断开并通知应用层之前,要发送的未被确认的探测数量

请记住,即使在内核中配置了 keepalive 支持,它也不是 Linux 中的默认行为。程序必须使用setsockopt接口为其套接字请求 keepalive 控制。实现 keepalive 的程序相对较少,但是您可以按照本文档后面解释的说明轻松地为大多数程序添加 keepalive 支持。

3.1. 配置内核

有两种方法可以通过用户空间命令在内核内部配置 keepalive 参数

我们主要讨论如何在 procfs 接口上完成此操作,因为它是最常用、推荐且最容易理解的。sysctl 接口,特别是关于 sysctl(2) 系统调用而不是 sysctl(8) 工具,仅在此处用于提供背景知识。

3.1.1.procfs接口

此接口需要sysctlprocfs构建到内核中,并且procfs挂载在文件系统中的某个位置(通常在/proc,如下面的示例所示)。您可以通过 "cat"/proc/sys/net/ipv4/目录中的文件来读取实际参数的值

  # cat /proc/sys/net/ipv4/tcp_keepalive_time
  7200

  # cat /proc/sys/net/ipv4/tcp_keepalive_intvl
  75

  # cat /proc/sys/net/ipv4/tcp_keepalive_probes
  9
        

前两个参数以秒为单位表示,最后一个是纯数字。这意味着 keepalive 例程在发送第一个 keepalive 探测之前等待两个小时(7200 秒),然后每 75 秒重新发送一次。如果连续九次未收到 ACK 响应,则连接被标记为断开。

修改此值非常简单:您需要将新值写入文件。假设您决定配置主机,以便 keepalive 在通道不活动十分钟后开始,然后以一分钟的间隔发送探测。由于我们的网络主干的高度不稳定性和间隔的低值,假设您还想将探测次数增加到 20。

以下是我们如何更改设置的方法

  # echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time

  # echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl

  # echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probes
        

为确保一切成功,请重新检查文件并确认这些新值显示在旧值的位置。

请记住procfs处理特殊文件,您不能对它们执行任何类型的操作,因为它们只是内核空间中的一个接口,而不是真实文件,因此请在使用脚本之前进行尝试,并尝试使用简单的访问方法,如前面示例中所示。

您可以通过 sysctl(8) 工具访问该接口,指定您要读取或写入的内容。

  # sysctl \
  > net.ipv4.tcp_keepalive_time \
  > net.ipv4.tcp_keepalive_intvl \
  > net.ipv4.tcp_keepalive_probes
  net.ipv4.tcp_keepalive_time = 7200
  net.ipv4.tcp_keepalive_intvl = 75
  net.ipv4.tcp_keepalive_probes = 9
        

请注意sysctl名称与procfs路径非常接近。写入是使用-w开关执行的 sysctl (8)

  # sysctl -w \
  > net.ipv4.tcp_keepalive_time=600 \
  > net.ipv4.tcp_keepalive_intvl=60 \
  > net.ipv4.tcp_keepalive_probes=20
  net.ipv4.tcp_keepalive_time = 600
  net.ipv4.tcp_keepalive_intvl = 60
  net.ipv4.tcp_keepalive_probes = 20
        

请注意,sysctl (8) 不使用 sysctl(2) 系统调用,而是直接在procfs子树中进行读写,因此您将需要procfs在内核中启用并在文件系统中挂载,就像您直接访问procfs接口中的文件一样。 Sysctl(8) 只是执行相同操作的另一种方式。

3.1.2.sysctl接口

还有另一种访问内核变量的方法:sysctl(2 ) 系统调用。当您没有procfs可用时,它可能很有用,因为与内核的通信是通过系统调用直接执行的,而不是通过procfs子树。目前没有程序包装此系统调用(请记住 sysctl(8) 不使用它)。

有关使用 sysctl(2) 的更多详细信息,请参阅手册页。

3.2. 使更改在重启后持久化

有几种方法可以在每次系统启动时重新配置系统。首先,请记住,每个 Linux 发行版都有自己的一组由 init (8) 调用的 init 脚本。最常见的配置包括/etc/rc.d/目录,或替代方案/etc/init.d/。在任何情况下,您都可以在任何启动脚本中设置参数,因为 keepalive 每次在其过程需要它们时都会重新读取这些值。因此,如果您更改了tcp_keepalive_intvl的值,当连接仍然建立时,内核将在未来使用新值。

有三个逻辑上应该放置初始化命令的位置:第一个是配置网络的位置,第二个是rc.local脚本,通常包含在所有发行版中,它被称为完成用户配置设置的位置。第三个位置可能已存在于您的系统中。回顾 sysctl (8) 工具,您可以看到-p开关从/etc/sysctl.conf配置文件加载设置。在许多情况下,您的 init 脚本已经执行 sysctl-p(您可以 "grep" 在配置目录中进行确认),因此您只需在/etc/sysctl.conf中添加行,以使它们在每次启动时加载。有关 sysctl.conf(5) 语法的更多信息,请参阅手册页。