go中TCP心跳核心是定期发送轻量数据并配合读写超时判断存活,需用SetWriteDeadline防止阻塞,服务端应验证pong响应,可启用系统级KeepAlive兜底,推荐30~60秒间隔。

在 Go 中实现 TCP 心跳机制,核心是通过定期发送轻量数据(如空字节、固定字符串)来维持连接活跃,并配合读写超时与错误检测判断对端是否存活。关键不在于“发什么”,而在于“怎么发、何时判、如何恢复”。
使用 SetDeadline + 定期 Write 实现简单心跳
客户端或服务端在连接建立后,启动一个 goroutine 每隔固定时间(如 30 秒)向对方写入一个字节(如 x00 或 "ping")。注意必须调用 conn.SetWriteDeadline(),否则阻塞写可能卡死。
- 每次写前设置写超时(如 5 秒):若对端关闭或网络中断,
Write()会立即返回 Error - 写成功后重置超时,继续下一轮;写失败(如
write: broken pipe或i/o timeout)则关闭连接并触发重连逻辑 - 避免只依赖
SetReadDeadline—— 对端静默断连时,读超时可能迟迟不触发
服务端需同时处理读超时与心跳响应
服务端不能只发心跳,还要验证对方是否响应。常见做法是要求对端收到 "ping" 后必须回复 "pong",并在规定时间内完成读取。
- 每次收到数据先检查是否为
"ping",若是则立即回写"pong" - 对每个连接维护独立的读超时(如 45 秒),超时未收到任何数据(包括 pong)即判定失联
- 不要把心跳包和业务包混用同一个超时策略——业务处理慢不应导致心跳误判
利用 net.Conn 的底层控制提升健壮性
标准 TCP 层本身支持 KeepAlive,Go 的 net.Conn 可通过 *TCPConn 启用系统级保活:
立即学习“go语言免费学习笔记(深入)”;
- 类型断言获取底层 TCP 连接:
tcpConn := conn.(*net.TCPConn) - 启用 OS 级心跳:
tcpConn.SetKeepAlive(true),并设间隔(如 60 秒):tcpConn.SetKeepAlivePeriod(60 * time.Second) - 注意:OS 级 keepalive 是兜底手段,不可替代应用层心跳——它不保证业务层可达,且默认周期长、行为不可控
封装成可复用的心跳管理器
把心跳逻辑抽离为结构体,支持启动/停止、自定义间隔、回调通知,避免每个连接重复写 goroutine。
- 字段包含:
conn net.Conn、pingTicker *time.Ticker、pongTimeout time.Duration、onDead func() - 启动时同时运行 ping goroutine 和 pong 监听循环(用
SetReadDeadline配合Read()) - 检测到异常后调用
onDead,由上层决定关闭、重连或告警
基本上就这些。心跳不是越频繁越好,30~60 秒间隔、搭配 5~10 秒超时,在多数内网/公网场景中足够平衡及时性与开销。真正难的是异常后的状态清理与自动恢复,这部分得结合你的业务连接模型来设计。