Go如何检测网络连接是否断开_Go连接状态判断方式

io.EOF 表示对端正常关闭,net.ErrClosed 表示本地已关闭,syscall.ECONNRESET 等才是网络异常中断的真实错误;应使用 errors.Is/errors.As 精准判断,而非字符串匹配。

io.EOF 是对端关闭连接的明确信号,net.ErrClosed 表示本地已调用 conn.Close(),而 syscall.ECONNRESETsyscall.ETIMEDOUT 等底层错误码才是网络异常中断(如断网、防火墙拦截、服务崩溃)的真实指纹——靠字符串匹配 "connection reset""use of closed" 会漏判、误判,且无法跨平台兼容。

读写时如何准确识别连接断开

Go 的网络错误不是“字符串日志”,而是可类型断言的结构化值。关键不是看错误文本,而是用 errors.Iserrors.As 做精准分类:

  • io.EOF:读操作返回此值,说明对端已关闭写方向(如调用了 close()),属于**正常断连**,应退出读循环并清理资源
  • errors.Is(err, net.ErrClosed):写操作返回此值,代表本地连接已被显式关闭,不能再读写
  • *net.OpError:绝大多数异常错误都包装在此类型中。需先 errors.As(err, &netErr)

    取,再判断:
    • netErr.Timeout() → 超时(含 context.DeadlineExceeded
    • netErr.Temporary() → 临时性错误(如 syscall.EAGAIN),可重试
    • errors.Is(netErr.Err, syscall.ECONNRESET) → 连接被对端强制重置(常见于进程 crash 或中间设备切断)
    • errors.Is(netErr.Err, syscall.ENETUNREACH) → 网络不可达(路由失效、网卡 down)

不发数据怎么知道连接还活着?

TCP 层的 KeepAlive 默认间隔长(Linux 通常 2 小时),无法满足应用层快速感知需求。必须在应用层主动探测:

  • 对 MySQL:使用 go-sql-driver/mysqlCheckConnLiveness = true 配置,它会在复用连接前调用底层 syscall.Read 检查套接字状态,遇到 syscall.EAGAIN 视为正常,io.EOF 或其他错误则标记连接失效
  • 对 WebSocket:用 gorilla/websocketSetPongHandler 更新最后活跃时间,并启动 goroutine 定期调用 WriteControl(websocket.PingMessage, ...);若 WriteControl 失败或 ReadMessage 返回 websocket.IsUnexpectedCloseError,即判定断开
  • 通用 TCP 连接:可封装一个非阻塞探测函数,类似 MySQL 驱动的 connCheck,通过 rawConn.Read(...) 尝试读 1 字节,根据 n == 0 && err == nil(EOF)、err == syscall.EAGAIN(正常空闲)、err != nil(异常)三态判断

为什么 net.Dial 成功不代表连接可用?

net.Dial("tcp", addr) 只完成三次握手,返回 nil 错误仅表示连接建立成功,**不保证后续读写一定通**。真实场景中常见问题:

  • 连接建立后,中间 NAT/防火墙在空闲超时后单向丢弃包,但 TCP 状态仍显示 ESTABLISHED
  • 服务端进程崩溃,但 FIN 包未发出,客户端 Read 会一直阻塞(除非设了 deadline)
  • 云环境 LB 主动踢掉空闲连接,客户端无感知

因此,任何长连接都必须配合:读写 deadline + 心跳探测 + 错误分类处理。例如设置 conn.SetReadDeadline(time.Now().Add(30 * time.Second)),并在每次 Read 后检查是否是 net.ErrorTimeout() == true

容易踩的坑:跨平台与兼容性盲区

不是所有系统都支持底层 socket 探测:

  • connCheck 在 Linux/macOS 可用 syscall.Read,但在 Windows 或某些容器环境(如部分 musl libc 镜像)会 fallback 到空实现(直接返回 nil),导致心跳失效
  • syscall.ECONNRESET 在 Windows 上对应的是 wsa.WSAECONNRESET,不能直接用 errors.Is(err, syscall.ECONNRESET) 判断,需用 x/sys/windows 中的错误码或统一走 netErr.Timeout()/netErr.Temporary() 分支
  • MySQL 驱动的 ReadTimeout 配置只影响 connCheck 探测阶段,不影响实际查询;若没设,探测可能永久阻塞

最稳妥的做法是:始终设 deadline;优先用 errors.Is 判断标准错误变量;对非标准 syscall 错误,结合 netErr.Op"read"/"write")和 Temporary() 做兜底策略。别信连接“看起来还开着”。