Fix(client): harden TCP heartbeat against half-dead connections

This commit is contained in:
yuanyuanxiang
2026-05-20 15:10:43 +02:00
parent 707dcdbbb4
commit e264e092f6
2 changed files with 54 additions and 0 deletions

View File

@@ -86,6 +86,27 @@ BOOL SetKeepAliveOptions(int socket, int nKeepAliveSec = 180)
}
#endif
// TCP_USER_TIMEOUT (RFC 5482): 未被对端 ACK 的已发数据超过此时间,内核直接把
// socket 标记为 ETIMEDOUT下一次 send/recv 立即报错。
//
// 为什么 SO_KEEPALIVE 不够keep-alive 只在连接完全 idle 时才探测,应用层每
// 30s 一次心跳让 TCP 永远进不了 idle 态。VM 挂起恢复 / 笔记本合盖唤醒 / NAT
// 表项老化等场景下,对端早已关闭连接但本端 send() 仍把字节塞进 SNDBUF 立即
// 返回成功——出现 ESTABLISHED + Send-Q 堆积的"半死连接",应用层完全无感,
// 默认要等 tcp_retries2 跑完(~15分钟)才报错。
//
// 选 30s>= 默认心跳间隔(5-30s)< 服务端 CheckHeartbeat 超时(>=60s)。
// Linux 2.6.37+ 支持macOS / 老内核 无此宏,自动跳过——那条路径上靠应用层
// ACK 看门狗(linux/main.cpp 心跳循环)兜底。
#ifdef TCP_USER_TIMEOUT
unsigned int userTimeoutMs = 30000;
if (setsockopt(socket, IPPROTO_TCP, TCP_USER_TIMEOUT,
&userTimeoutMs, sizeof(userTimeoutMs)) < 0) {
Mprintf("Failed to set TCP_USER_TIMEOUT\n");
// 非致命keep-alive 已设上,应用层还有 ACK 看门狗兜底,继续即可
}
#endif
Mprintf("TCP keep-alive settings applied successfully\n");
return TRUE;
}