本文目录导读:

TCP Keepalive 是一种 保活探测机制,用于检测 TCP 连接的对端是否仍然存活(是否发生网络故障、进程崩溃或断电)。
其核心原理是:当一条 TCP 连接长时间空闲(无数据交换)时,内核会启动定时器,定期发送一个空的数据段(ACK)探测包,根据对端的回复来判断连接状态。
以下是详细的保活工作流程和机制:
关键参数
TCP Keepalive 的行为由三个内核参数控制(在 Linux 系统中):
tcp_keepalive_time(默认 7200秒,即 2小时):- 含义:连接空闲多久后,内核开始发送第一个探测包。
- 如果你的应用长时间不发送数据,2小时候才会开始探测。
tcp_keepalive_intvl(默认 75秒):含义:如果上一个探测包没有收到回应,每隔多少秒重发一次探测包。
tcp_keepalive_probes(默认 9次):含义:连续发送多少次探测包后,如果仍无回应,就认为连接已断开。
保活的具体工作流程(以 Linux 为例)
假设你建立了一个TCP连接,双方都不发送数据,处于空闲状态。
-
静默期:
- 连接建立后,双方无数据交换。
- 计时器开始计时,持续
tcp_keepalive_time(默认 2小时)。 - 注意:这2小时内,网络上 绝对不会有任何 Keepalive 包,这是为了节省带宽和降低干扰。
-
探测阶段(第1次探测):
- 2小时到了,内核会发送第一个 Keepalive 探测包。
- 这个包是 一个特殊的、长度为0的TCP报文段,ACK 标志位被置位,确认序号为对方发来的最后一个数据包的序号。
-
根据对端响应处理:
-
情况A:对端正常存活
- 对端会回复一个 ACK。
- 内核收到 ACK 后,重置保活计时器,重新开始计时(再次等待 2小时)。
- 应用层完全无感知。
-
情况B:对端进程崩溃/挂起(但对端的操作系统还在运行)
- 对端的操作系统内核会替挂起的进程回复一个 RST(Reset) 包。
- 本端内核收到 RST 后,立即关闭该 TCP 连接,并通知应用层(
read()返回 -1,errno为ECONNRESET)。 - 保活成功:检测到了对端进程死亡。
-
情况C:对端主机崩溃/断电/网络中断(无任何响应)
- 本端发送的探测包石沉大海。
- 本端等待
tcp_keepalive_intvl(75秒),没有收到回复,再次发送第2个探测包。 - 如果连续发送
tcp_keepalive_probes(9次)都没有收到任何回复(包括 RST 或 ACK)。 - 总耗时:
tcp_keepalive_time + tcp_keepalive_probes * tcp_keepalive_intvl= 7200 + 9 * 75 = 7875秒 ≈ 2小时11分钟。 - 时间耗尽后,内核关闭连接,并通知应用层(
read()返回 0,或者write()返回 -1,errno为ETIMEOUT)。
-
如何启用 Keepalive?
默认情况下,TCP Socket 的 Keepalive 是 关闭 的,如果你想让应用使用(而不想等2小时),需要主动开启。
通过 Socket 选项(推荐)
-
在服务端或客户端代码中,使用
setsockopt函数,设置SO_KEEPALIVE选项为 1。// C 语言示例 int keepalive = 1; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); // 通常还会设置自定义的超时时间(代替系统默认2小时) int idle = 10; // 空闲10秒后开始探测 int interval = 5; // 每5秒发一次探测 int count = 3; // 连续3次失败后断开连接 setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &count, sizeof(count));
-
总超时时间:
idle(10秒) +count(3) *interval(5) = 25秒,这样就实现了“如果25秒内对端无响应,就断开连接”,比默认的2小时快得多。
修改系统参数(影响所有未自定义的 Socket)
- 修改
/etc/sysctl.conf,对所有未调用setsockopt开启的 socket 生效:# 空闲60秒开始探测 net.ipv4.tcp_keepalive_time = 60 # 探测间隔10秒 net.ipv4.tcp_keepalive_intvl = 10 # 探测次数3次 net.ipv4.tcp_keepalive_probes = 3
- 然后执行
sysctl -p使其生效。
Keepalive 的优缺点
优点:
- 自动清理:无需应用层做心跳,内核自动检测并回收死掉的连接,避免资源泄漏。
- 简单可靠:内核实现,稳定且对应用透明(只需开启选项)。
- 粒度可控:可以通过
setsockopt精细控制探测的频率和超时。
缺点:
- 默认时间过长:默认 2小时的空闲时间,对大多数应用来说不够及时。
- 消耗带宽:频繁探测会占用少量带宽,但在局域网内几乎无影响。
- 预测延迟:无法准确预测对端何时会死,只能等超时,对端刚死,你的探测包可能正好在1秒后发出,也可能在2小时后才发出(取决于
tcp_keepalive_time设置)。 - 半开连接:只能检测对端是否可达和操作系统是否回复,如果对端应用卡死但不崩溃(操作系统仍在,会回复 ACK),Keepalive 无法检测到,这种情况下需要应用层心跳。
核心一句话:TCP Keepalive 不是应用层心跳的替代品,而是一种系统级的、用于清理死连接的保活机制。 对于需要快速检测对端状态的应用(如即时通讯、游戏、实时交易),通常建议在应用层实现自定义的心跳协议,其响应速度远比 TCP Keepalive 快(即使调参后)。
标签: tcp保活机制