应使用 gorilla/websocket 的 upgrader.upgrade() 启动服务,禁用手动 http 响应;读写需分离 goroutine,用 readmessage/writemessage 处理消息,设读写超时,连接断开时主动清理资源。

用 gorilla/websocket 启动一个基础服务,别碰 net/http 原生实现
Go 官方 net/http 没有 WebSocket 支持,硬写握手和帧解析极易出错。直接上 gorilla/websocket——它不是“第三方可选”,而是事实标准。它的 Upgrader 封装了所有协议细节(包括 Origin 校验、Sec-WebSocket-Key 生成、HTTP 状态码响应),你只需专注业务逻辑。
常见错误:自己用 http.ResponseWriter 写 101 Switching Protocols,漏掉 Connection: upgrade 或 Upgrade: websocket 头,浏览器报 WebSocket connection to 'xxx' failed。
- 必须用
Upgrader.Upgrade(),不能手动写响应头 -
Upgrader.CheckOrigin默认拒绝非同源请求,开发时设为func(r *http.Request) bool { return true },上线前务必改回严格校验 - 每个连接要单独起 goroutine 处理读/写,避免阻塞其他连接
读消息时用 conn.ReadMessage(),别循环调 conn.Read()
ReadMessage() 自动处理 WebSocket 帧解包、掩码解密、碎片重组;而 Read() 是底层字节流操作,需手动解析 FIN、opcode、mask、payload length —— 极易因边界判断错误导致 panic 或丢数据。
典型现象:前端发一条文本消息,后端收到空字符串或乱码,日志里出现 websocket: bad write message type 或 unexpected EOF。
-
ReadMessage()返回messageType, data, error,messageType通常是websocket.TextMessage或websocket.BinaryMessage - 不要在同一个 goroutine 里连续调
ReadMessage()和WriteMessage(),可能触发并发写 panic;读写应分离到不同 goroutine,或加锁 - 超时控制用
conn.SetReadDeadline(),不是靠time.After包裹读操作
写消息必须用 conn.WriteMessage(),且注意 WriteDeadline
WriteMessage() 负责帧封装、掩码(服务端不掩码,但函数内部会忽略)、缓冲区管理。手动拼帧或用 Write() 写原始字节,大概率触发 websocket: close sent 或连接静默断开。
容易被忽略的点:默认写操作无超时,客户端网络卡顿时,WriteMessage() 可能无限阻塞,拖垮整个 goroutine。
- 务必在写前调
conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - 如果要广播消息,别对每个
conn串行WriteMessage(),用带缓冲的 channel 分发,每个连接 goroutine 自行消费 - 发送大消息(> 1MB)前先检查
conn.WriteBufferPool配置,否则可能频繁分配内存
连接生命周期管理:别依赖 defer conn.Close()
goroutine 退出时 defer conn.Close() 看似稳妥,但若连接已断(如客户端关页),再调 Close() 会返回 websocket: close sent 错误,且无法触发真正的清理逻辑(如从 map 中删连接、释放资源)。
真实场景中,你要感知断开、清理状态、通知其他模块 —— 这些都得在 ReadMessage() 返回非 nil error 时立刻做。
- 把
conn和关联数据(如用户 ID、房间号)存在 struct 里,错误处理统一入口 -
io.EOF和websocket.CloseMessage是正常断开;net.OpError或websocket.ErrCloseSent往往意味着异常中断 - 关闭前调
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, "")),让客户端收到标准关闭帧
最麻烦的从来不是怎么连上,而是怎么知道它什么时候真断了、谁该负责清理、重连时状态怎么同步——这些没想清楚,服务跑三天就内存暴涨或 goroutine 泄露。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











