TCP 原生 Keepalive 功能的使用
心跳与连接假死
在使用 TCP 协议开发网络应用时,经常会遇到一个棘手问题:客户端与服务端建立连接后,长时间空闲(没有数据交互)可能导致连接看似正常,但实际上已经不可用,俗称“连接假死”。
通常的解决方案是应用层引入“心跳机制”,定期发送自定义消息检测连接状态。然而,若服务端没有提供无害操作,且我们无法修改服务端代码时,这时应该怎么办?这时就可以考虑使用操作系统内核提供的 TCP Keepalive 功能。
TCP Keepalive
TCP Keepalive 是 TCP 协议栈自带的一种机制,由操作系统内核实现。它能在 TCP 连接长时间空闲时,自动向对端发送探测包,检测连接是否仍然有效。
其主要作用和通常的心跳功能一样:检测对端连接是否还活跃,避免“假死”连接,节约资源。
TCP Keepalive 的实现原理如下:
- 当 TCP 连接在一定时间内没有数据交互后(即空闲状态),TCP 协议栈会主动发送 Keepalive 探测包。
- 如果对端连接正常,会立即回应 ACK,表明连接仍然有效。
- 如果对端已经不可达或断开,探测包就不会收到回应。系统会多次重试探测,直到超过指定的次数后,认为连接失效,并关闭连接。
Linux 实现
以 Linux 操作系统为例,TCP Keepalive 有三个核心参数:
参数 | 含义 | 默认值 |
---|---|---|
tcp_keepalive_time |
连接空闲多久后发送第一个探测包 | 7200 秒(2小时) |
tcp_keepalive_intvl |
探测包发送间隔 | 75 秒 |
tcp_keepalive_probes |
探测失败多少次后断开连接 | 9 次 |
默认情况下,Linux Keepalive 的设置周期较长,并不适合大多数应用,需要根据实际需求进行调整。
下面以 Python 为例演示如何设置 Keepalive。
import socket
# 创建 TCP socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 开启 Keepalive
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# 配置 Keepalive 参数 (Linux)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 60秒空闲后发送探测包
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 探测间隔10秒
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) # 连续3次失败则关闭连接
# 连接服务器
s.connect(('example.com', 80))
# 后续使用 socket 进行通信,且此连接会自动检测心跳
TCP Keepalive 是系统级别的配置,精细控制程度不如应用层心跳机制。另外注意防火墙或 NAT 的影响。