SO_KEEPALIVE默认非实时心跳,而是2小时空闲后启动探测,需配合tcp_keepalive_time(建议5–10分钟)、tcp_keepalive_intvl(30–60秒)和tcp_keepalive_probes(3–5次)调优,并注意其仅对新建连接生效、全局配置且需root权限。

SO_KEEPALIVE 默认行为不等于“每秒心跳”
很多开发者误以为开启 SO_KEEPALIVE 就能立即触发高频心跳,实际它在 Linux 上默认是 2 小时无数据后才开始探测,且探测间隔和重试次数都不可控。Windows 行为略有不同,但同样不满足实时性要求。关键点在于:SO_KEEPALIVE 只是开关,真正控制时间参数的是内核级 TCP 栈配置(如 tcp_keepalive_time),普通用户态程序无法直接设毫秒级间隔。
Linux 下用 setsockopt 启用并配合 sysctl 微调
启用本身很简单,但要精确控制,必须分两步:先开 socket 选项,再改系统参数(影响所有 TCP 连接)。注意:后者需 root 权限,且是全局生效的。
-
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))开启基础保活 - 修改
/proc/sys/net/ipv4/tcp_keepalive_time(单位秒,如设为 60) - 对应调整
tcp_keepalive_intvl(两次探测间隔)和tcp_keepalive_probes(失败后重试次数) - 这些值对已建立连接**不生效**,只影响后续新建连接
想 per-socket 精确控制?只能用 TCP_USER_TIMEOUT 或应用层心跳
Linux 从 2.6.37 起支持 TCP_USER_TIMEOUT(单位毫秒),但它控制的是“发送队列中数据未被 ACK 的最大等待时间”,不是心跳间隔;而真正按需发心跳、自定义间隔、携带业务 payload 的方案,必须自己实现应用层心跳。常见做法:
- 用
epoll或select配合定时器(如timerfd_create)驱动心跳发送 - 每次心跳后重置超时计时器,避免与内核 keepalive 冲突
- 服务端收到心跳后应主动
send响应,防止 NAT 设备丢弃单向包 - 不要依赖
SO_KEEPALIVE检测“对方进程崩溃”,它检测的是“链路断开或对端关闭连接”
跨平台兼容性陷阱:Windows 的 keepalive 行为差异
Windows 的 setsockopt 支持 TCP_KEEPALIVE(注意不是 SO_KEEPALIVE)扩展结构体,可传入毫秒级 keepalivetime 和 keepaliveinterval,但仅对当前 socket 生效。然而:
立即学习“C++免费学习笔记(深入)”;
- 该扩展在 MinGW / MSVC 头文件中常未正确定义,需手动补全
TCP_KEEPALIVE结构 - Windows 10 1809+ 才完全支持用户态设置 interval;旧版本即使设了也按固定 1 秒间隔执行
- macOS 不支持任何 per-socket 时间参数,完全依赖
net.inet.tcp.keepidle等 sysctl
跨平台项目若需要一致行为,应用层心跳仍是唯一可靠路径。内核 keepalive 的“精确微调”本质上是个伪需求——它本就不是为低延迟场景设计的。


















