用 gorilla/websocket 连 WebSocket 服务最稳,因其成熟、维护勤、文档实,能可靠处理握手、ping/pong、分帧、重连等细节;务必用 ws:// 或 wss:// URL,独立 goroutine 读消息,加锁或通道写消息,启用自动 pong 响应并定时 ping,重连需退避,关闭前先发 CloseMessage。

用 gorilla/websocket 连 WebSocket 服务最稳
Go 官方标准库不带 WebSocket 客户端,直接上 gorilla/websocket 是目前最成熟、维护最勤、文档最实的选择。别试 gobwas/ws 或自己封装 net/http —— 握手、ping/pong、分帧、重连这些细节它都兜住了。
常见错误现象:dial tcp: lookup example.com: no such host 或 websocket: bad handshake,多数是 URL 写成 http:// 而不是 ws://(或 wss://);也可能是服务端没开 WebSocket 升级支持,只返回了 200 HTML 页面。
- 初始化连接必须用
websocket.DefaultDialer.Dial(),传入完整 URL,比如"ws://localhost:8080/ws" - 如果服务端需要认证,把 token 放在
http.Header里传给Dial(),别拼在 URL 查询参数里(有些代理会丢) -
wss://场景下若用自签名证书,需提前设置tls.Config{InsecureSkipVerify: true}到Dialer.TLSClientConfig
收发消息时别卡死在 ReadMessage() 或 WriteMessage()
这两个函数默认是阻塞的。客户端一连上就立刻 ReadMessage() 等消息?错——服务端可能几秒后才推,你线程就挂那儿了,连心跳都发不出。
典型表现:程序看似运行,但收不到后续消息;或者写消息时卡住,整个 goroutine 僵死。
- 读消息务必起独立 goroutine,用
for { _, _, err := conn.ReadMessage() }循环处理 - 写消息不能直接在读循环里调
WriteMessage()——gorilla/websocket的 conn 不是并发安全的,要加锁或走WriteChannel() - 更稳妥的做法:用一个
chan []byte做写队列,另起 goroutine 拿到消息后统一WriteMessage(),避免并发写 panic
心跳和重连得自己补,gorilla/websocket 不自动做
没有心跳,NAT、代理、云负载均衡器大概率 60 秒左右就静默断连;断了也不报错,ReadMessage() 就一直卡着,直到你手动检测。
容易被忽略的点:服务端发 Ping 时,客户端必须响应 Pong,否则有些服务端(如 Nginx + WebSocket)会主动踢掉连接。
- 启用自动 pong 响应:调
conn.SetPongHandler(conn.PongHandler()),别漏这句 - 自己发 ping:每 30 秒调一次
conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(10*time.Second)) - 重连逻辑别放
defer conn.Close()后面——连接断了,defer才触发,但重连要立刻开始。建议用带退避的 for-select 循环,失败后 sleep 后再 dial
关闭连接前记得发 CloseMessage,不然服务端可能等超时才清理
直接 conn.Close() 是粗暴的 TCP 断开,服务端收不到关闭帧,会以为客户端异常掉线,可能保留 session 或资源几分钟。
真实场景中,用户切页面、App 进后台、Ctrl+C 中断,都要体面关连。
- 先发关闭帧:
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - 然后设个短超时(比如 5 秒),再调
conn.Close(),防止 WriteMessage 卡住 - 如果是在信号捕获(如
os.Interrupt)里关连,确保 goroutine 已退出,否则WriteMessage()可能 panic:write to closed connection
WebSocket 客户端真正难的不是连上,而是连得久、断得清、消息不丢。所有“看起来跑通了”的代码,放到弱网、长连接、服务重启场景下,八成会在心跳、重连、关闭这几步露馅。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











