net.conn不会自动重连,必须手动重建;其为一次性对象,关闭或异常断开后read/write即报错,需通过外层循环、错误分类、指数退避、心跳检测及状态同步等机制实现安全重连。

连接断开后 net.Conn 不会自动重连,必须手动重建
Go 的标准库 net.Conn 是一次性对象:一旦底层 TCP 连接关闭(如对端崩溃、网络闪断、防火墙超时),conn.Read() 或 conn.Write() 会返回 io.EOF 或 net.OpError,且该 conn 对象不可再用。你不能调用 conn.Reconnect()——根本不存在这个方法。
实操建议:
- 所有读写操作必须包裹在
for循环中,并检查错误类型;遇到连接失效错误(如io.EOF、net.ErrClosed、syscall.ECONNRESET)就跳出循环 - 重连逻辑不能写在读/写函数内部,而应由外层控制流统一管理,避免重复建连或竞态
- 不要复用已关闭的
conn,每次重连都调用net.Dial()或tls.Dial()新建实例
重试策略要区分临时错误和永久错误
不是所有错误都该重试。比如 dial tcp 10.0.0.1:8080: connect: connection refused 可能是服务未启动,重试几次后应暂停;而 read: connection reset by peer 更可能是瞬时网络抖动,适合快速重试。
实操建议:
- 对
net.OpError检查Err字段:若为syscall.ECONNREFUSED、syscall.ENETUNREACH,属于“初始不可达”,建议指数退避 + 最大重试次数限制(如 5 次,间隔从 100ms 起翻倍) - 若错误是
io.EOF或syscall.EPIPE,说明连接曾建立成功,大概率是运行时中断,可立即重试(甚至不退避) - 使用
errors.Is(err, io.EOF)和errors.Is(err, syscall.ECONNREFUSED)判断,别依赖err.Error()字符串匹配
心跳检测不能只靠 SetDeadline,得主动发 ping
SetDeadline 只控制单次读/写超时,无法感知“连接静默存活”。例如对端进程卡死但 TCP 连接未关闭,你的 Read() 会永远阻塞(除非设了 deadline),而对方其实早已失联。
实操建议:
- 启用
SetKeepAlive(Linux 默认开启,但 Windows 需手动设)仅解决底层 TCP keepalive,粒度粗(默认 2 小时),无法满足秒级故障发现 - 必须实现应用层心跳:定期向对端发送轻量
PING帧(如 JSON{"type":"ping"}),并等待PONG回应;超时即判定连接失效 - 心跳协程与业务读写协程需共享同一个
context.Context,确保连接关闭时心跳 goroutine 能及时退出,避免泄漏
并发安全的重连状态需要显式同步
多个 goroutine 同时尝试重连(比如读失败触发一次、心跳超时又触发一次),可能并发 dial,造成资源浪费甚至服务端拒绝连接。
实操建议:
- 用
sync.Mutex或sync.Once控制重连入口:只有第一个发现断连的 goroutine 执行 dial,其余等待或直接返回 - 维护一个原子状态变量(如
int32表示Disconnected/Connecting/Connected),避免状态竞争 - 重连成功后,务必替换旧的
conn指针,并广播新连接可用——老的读写 goroutine 应监听该信号并退出,否则可能继续往已关闭的 conn 写数据,触发 panic
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











