TCP KeepAlive需显式启用SO_KEEPALIVE并调优三个内核参数(time/intvl/probes)才能有效探测死连接,仅开启选项默认2小时才探测,远超中间设备超时阈值。

TCP KeepAlive 是内核级的死连接探测机制,不是一开就“保活”的开关,而是针对空闲连接被动发包检测对端是否还活着。要真正用它检测死连接句柄(即已失效但本地 socket 仍处于 ESTABLISHED 状态的连接),必须满足两个前提:
✅ 应用层显式启用了 SO_KEEPALIVE;
✅ 内核参数或套接字级别设定了合理的时间窗口,避免被中间设备(NAT、LB、防火墙)提前断连。
确认并启用 SO_KEEPALIVE(关键第一步)
KeepAlive 默认是关闭的。不调用 setsockopt(..., SO_KEEPALIVE, &on, ...),内核根本不会启动探测逻辑,无论 /proc 参数怎么改都无效。
-
C/Go/C++ 等底层语言:
int enable = 1; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
-
Java(Netty):
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
-
Python(socket):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
⚠️ 注意:只设这个还不够——默认探测周期仍是 2 小时起步,远慢于多数网络设备超时阈值。
调优三个核心参数(让探测真正有用)
Linux 用三个参数控制探测节奏,单位均为秒/次数:
| 参数 | 含义 | 默认值 | 建议值(典型场景) |
|---|---|---|---|
tcp_keepalive_time |
空闲多久后发第一个探测包 | 7200(2小时) |
600(10分钟) |
tcp_keepalive_intvl |
每次探测失败后隔多久重发 | 75秒 |
30(30秒) |
tcp_keepalive_probes |
连续失败多少次才断连 | 9次 |
3(3次) |
总失效时间 ≈ time + (probes − 1) × intvl → 示例中为 600 + 2×30 = 660秒(11分钟)。
配置方式(任选其一):
-
临时生效(重启失效):
sudo sysctl -w net.ipv4.tcp_keepalive_time=600 sudo sysctl -w net.ipv4.tcp_keepalive_intvl=30 sudo sysctl -w net.ipv4.tcp_keepalive_probes=3
-
永久生效(写入配置文件):
编辑/etc/sysctl.conf,追加:net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3
然后运行
sudo sysctl -p加载。 -
单 socket 精准控制(推荐高并发服务):
int idle = 600, interval = 30, probes = 3; setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &probes, sizeof(probes));
✅ 优势:不影响其他连接(如短 HTTP 请求),避免全局参数“一刀切”。
验证是否真起作用
别只看 ss -tni 输出里的 keepalive 字样——那只是表示选项已开启,不代表探测正在发生。
-
抓包确认(最可靠):
sudo tcpdump -i any 'tcp[tcpflags] & (tcp-syn|tcp-rst|tcp-fin) == 0 and src port <your_port>' -w keepalive.pcap
空闲一段时间后查看是否有空 ACK 或带数据的保活段发出。
-
检查当前值:
cat /proc/sys/net/ipv4/tcp_keepalive_time ss -i dst <remote_ip>:<port> | grep -i keep
重要提醒:KeepAlive ≠ 应用层存活
它只能告诉你“对端主机网络可达、TCP 栈还在收包”,但无法判断:
- 远程进程是否卡死、未处理请求;
- 中间四层负载均衡器(如 AWS ALB、SLB)是否已老化连接;
- 防火墙是否静默丢弃了 keepalive 探测包(尤其开启
tcp_timestamps时)。
所以生产环境必须:
- 在协议层加轻量心跳(如 HTTP/2 PING、gRPC keepalive、Redis PING);
- 由业务代码主动发送 + 超时重试 + 断连重建;
- 把 TCP KeepAlive 当作最后一道兜底防线,而非唯一依赖。
不复杂,但容易忽略。

















