Go中无CloseStatus类型,gorilla/websocket关闭状态全在error中,须用websocket.IsCloseError()判断并提取码值。

Go 里没有 CloseStatus 类型,gorilla/websocket 不暴露关闭状态对象,所有关闭信息都藏在 error 里 —— 直接检查 err 是否为 websocket.CloseError,再用 websocket.IsCloseError() 判断码值,才是唯一可靠路径。
为什么不能用 CloseStatus 接口或结构体
你看到的 CloseStatus(比如 status.getCode())是 Spring Boot 的 Java 实现概念,Go 的 gorilla/websocket 库完全不提供类似抽象。它的关闭帧处理是 error-first 的:客户端发 CloseMessage,服务端 ReadMessage() 立即返回非 nil error;服务端主动关,也只通过 WriteMessage(websocket.CloseMessage, ...) 和后续 conn.Close() 完成。所有状态码(如 1000、1006)都作为 error 的底层字段存在,不可直接取属性。
-
websocket.CloseNormalClosure是常量,值为1000,不是运行时对象 -
websocket.CloseAbnormalClosure是1006,同样只是整数 - 试图把
err断言为某个 “status struct” 会编译失败或 panic
如何正确提取关闭码和原因
必须走 websocket.IsCloseError() + websocket.FormatCloseMessage() 这套组合。读出错后,先确认是不是关闭错误,再解析其码和原因字符串:
- 调用
websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway)判断是否为预期关闭请求 - 若为真,说明客户端发了 CloseMessage,此时可安全调用
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(...))回复 -
websocket.FormatCloseMessage(code, reason)的reason长度不能超 123 字节,否则会被截断或触发ErrBadWrite - 注意:
err.Error()里可能含 “close 1006” 字样,但不能靠字符串匹配——它不稳定,且 1006 只表示“没收到关闭帧”,不是具体原因
常见误判场景与对应处理
实际代码中,err 类型五花八门,必须分层判断,顺序不能错:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
err == nil:正常读到消息,继续循环 -
websocket.IsUnexpectedCloseError(err)成立:比如1006(ABNORMAL_CLOSURE)、1011(SERVER_ERROR),说明连接已断,应立即退出读 goroutine,不再尝试WriteMessage -
websocket.IsCloseError(err, ...)成立:说明收到合法关闭帧,此时可回复并 clean up -
net.ErrClosed或"use of closed network connection":发生在WriteMessage后,说明连接已在别处被关,不要再调conn.Close() -
io.EOF或"read: connection closed":通常伴随1006,必须立刻停止读循环,否则 goroutine 泄漏
主动关闭时如何指定关闭码和原因
服务端想优雅下线某个连接,不能只调 conn.Close() —— 这只会触发 TCP FIN,对方收到的是 1006。要发标准关闭帧:
- 先调
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, "server restarting")) - 写完后建议
time.Sleep(10 * time.Millisecond),给对方时间响应 pong 或关闭 - 再调
conn.Close()彻底释放资源 - 若
WriteMessage失败(如对方已断),直接conn.Close()即可,不必重试
真正容易被忽略的是:关闭帧的发送必须在连接还“活”的时候完成。一旦 ReadMessage 返回 1006,底层 net.Conn 很可能已关,再写就会 panic。

















