使用gorilla/websocket连接并发送消息需调用websocket.dial建立连接,设置authorization头进行jwt鉴权,启动独立读协程处理消息,用setwritedeadline控制写超时,封装带指数退避的重试发送函数,并校验json序列化结果与消息长度以确保可靠传输。

如何用 gorilla/websocket 连接并发送消息
Go 官方没有内置 WebSocket 客户端,gorilla/websocket 是最常用且稳定的选择。连接前必须处理 HTTP 升级响应,不能直接用 http.Get 或 net/http.Client 发起普通请求。
- 使用
websocket.Dial建立连接,传入 URL(如"ws://localhost:8080/ws")和可选的*websocket.Dialer - 务必检查返回的
*websocket.Conn和error,连接失败时conn为nil,直接调用WriteMessage会 panic - 若服务启用了 JWT 鉴权,需在
Dialer.Header中设置"Authorization"字段,例如:dialer.Header.Set("Authorization", "Bearer xxx") - 连接后建议立即启动读协程,否则服务端发来的 ping/pong 或业务消息可能堆积导致阻塞写操作
怎么封装一个带超时和错误重试的消息发送函数
裸调 conn.WriteMessage 缺乏容错能力,生产环境必须控制超时、区分临时错误与永久错误,并避免无限重试。
- 用
conn.SetWriteDeadline设置单次写入超时(如 5 秒),否则网络卡顿时WriteMessage可能长期阻塞 - 对
websocket.ErrCloseSent、websocket.ErrCloseReceived等明确关闭状态,应停止重试并清理资源 - 对
net.OpError(如连接中断)可有限重试(建议 ≤3 次),但要加指数退避,避免雪崩;对io.EOF说明连接已断,不应再尝试写入 - 封装示例:
func sendWithRetry(conn *websocket.Conn, msgType int, data []byte) error { conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) if err := conn.WriteMessage(msgType, data); err == nil { return nil } else if errors.Is(err, websocket.ErrCloseSent) || errors.Is(err, websocket.ErrCloseReceived) { return err } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { return fmt.Errorf("write timeout: %w", err) } return err }
如何安全地并发读取消息并分发给不同 handler
WebSocket 连接是全双工的,但 conn.ReadMessage 是阻塞调用,多个 goroutine 同时读会 panic;必须由单一读协程负责接收,再通过 channel 转发给业务逻辑。
- 启动一个独立 goroutine 执行循环
ReadMessage,捕获websocket.CloseMessage并主动调用conn.Close() - 定义
type Message struct { Type int; Data []byte },将原始消息解包后发送到chan Message - 业务层按需注册 handler:比如用
map[string]func([]byte)存储不同消息类型的回调函数,收到消息后根据Data解析出"type"字段再路由 - 注意
ReadMessage返回的[]byte是内部缓冲区切片,若需跨 goroutine 使用,必须拷贝(copy(dst, src)),否则可能被下一次读覆盖
为什么 JSON 序列化后要检查消息长度再发送
WebSocket 协议本身不限制单帧大小,但多数服务端(如 Nginx 代理、某些云 WAF)默认限制 64KB 左右,超长消息会被静默截断或直接断连。
- 发送前用
len(data)判断是否超过阈值(如 60 * 1024),超过则拆分成多帧或返回错误,不要依赖服务端报错后再处理 - 如果必须传大对象,优先考虑分块上传 + 标识字段(如
{"id":"abc","chunk":1,"total":3,"data":"..."}),由客户端拼接 - 避免在 handler 里直接
json.Marshal后无条件发送:空结构体或含 NaN 的 float64 会导致json.Marshal返回 error,不检查就传入WriteMessage会 panic - 推荐统一用
jsoniter.ConfigCompatibleWithStandardLibrary替代原生encoding/json,它对 NaN 处理更鲁棒,且性能更好
真正麻烦的不是连上或发出去,而是连接意外中断时 handler 没收到通知、重连后消息序号错乱、或者 JSON 字段名拼写错误导致前端静默失败——这些都得靠日志+结构体 tag 校验+双向心跳来兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











