readmessage返回websocket.closeabnormalclosure时应立即确认错误类型、停止读写、清理资源并主动关闭连接;主动关闭需先发closemessage帧再调conn.close();收到关闭帧须立即回应并退出循环。
readmessage返回websocket.closeabnormalclosure时该怎么做
这不是你代码出错,而是底层tcp连接已经断了——浏览器关页、nginx超时、手机切后台都可能触发。此时readmessage返回的err会是websocket.closeabnormalclosure(对应状态码1006),伴随read: connection closed或use of closed network connection。
必须立刻响应,不能忽略或仅log后继续循环:
- 先调用
websocket.IsCloseError(err, websocket.CloseAbnormalClosure)确认类型,别直接return - 检查连接是否已关闭:
conn.RemoteAddr()若panic说明conn已不可用,跳过后续操作 - 不要再调用
conn.WriteMessage——大概率panic;也不要defer conn.Close(),它此时可能已失效 - 清理关联资源:从
sync.Map中删除该连接、取消对应context、关闭通知channel
主动关闭连接时如何发CloseNormalClosure(1000)
想让对端收到1000而不是1006,关键不是调conn.Close(),而是先发关闭帧。直接conn.Close()只会断TCP,不走WebSocket握手流程。
正确顺序是:
- 调用
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "bye")) -
websocket.FormatCloseMessage第二个参数是原因字符串,长度不能超123字节,否则截断或报错 - 写完后等一小会(比如
time.Sleep(10 * time.Millisecond)),再调conn.Close() - 如果写关闭帧失败(如网络已断),就跳过,直接
conn.Close(),避免阻塞
收到客户端CloseMessage帧时怎么回应
客户端调ws.close(1000)时,并不会让ReadMessage返回一个普通消息,而是直接返回一个err,且websocket.IsCloseError(err, ...)为true。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
这时你要做的是回一个同类型的帧,完成握手:
- 用
websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway)判断是否为合法关闭请求 - 成立后立即调
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - 写完立刻
return,不要继续ReadMessage或WriteMessage,否则可能触发use of closed network connection - 注意:不要在
defer里处理这个逻辑,响应必须在读出错的当次函数调用内完成
为什么conn.Close()之后还收到read: connection closed
这是竞态现实,不是bug。你调了conn.Close(),但另一goroutine可能正卡在ReadMessage的系统调用里,下一次调用就会立刻返回该错误。
安全做法是靠协同机制提前退出读循环:
- 启动读goroutine时传入
context.Context,每次ReadMessage前先检查ctx.Err() != nil - 关闭时调
cancel(),而非只依赖conn.Close() - 对
WriteMessage设conn.SetWriteDeadline,防止因对端不响应而永久阻塞 -
conn.Close()本身不是线程安全的,多个goroutine并发调用可能panic,需加锁或用channel串行化
真正容易被忽略的是:关闭帧的发送时机和conn状态的双重校验。很多问题不是没发关闭帧,而是发晚了——读出错后conn已处于半关闭状态,再写就panic。必须在err != nil分支里第一时间判断是否还能写,不能假设“反正都要关了”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










