gorilla/websocket 是 go 生态唯一推荐的 websocket 实现,因标准库不支持、x/net/websocket 已归档且不兼容现代 tls/http/2;http.handlefunc 不能直接注册 websocket 路由,因其返回后 http 连接即关闭,而升级需接管底层 tcp 连接。

gorilla/websocket 是当前 Go 生态中唯一推荐的 WebSocket 实现,标准库不支持,golang.org/x/net/websocket 已归档多年、无维护、不兼容现代 TLS 和 HTTP/2,新项目必须用它。
为什么 http.HandleFunc 不能直接注册 WebSocket 路由
因为 http.HandleFunc 返回后,HTTP 连接立即关闭,而 WebSocket 升级需要接管底层 TCP 连接生命周期。浏览器会卡在 pending,或报 ERR_CONNECTION_CLOSED、服务端抛 websocket: bad handshake。
- 必须用
http.ServeMux或直接传给http.Server,并在 handler 中显式调用upgrader.Upgrade() -
upgrader实例应复用,不要每次请求都 new —— 它内部缓存了CheckOrigin回调等状态 - 升级前绝对不能向
w http.ResponseWriter写任何内容(包括空格、换行),否则触发http: multiple response.WriteHeader calls
服务端如何安全读写消息
*websocket.Conn 不是线程安全的:并发调 conn.WriteMessage 会 panic concurrent write to connection;阻塞读 conn.ReadMessage 会导致 goroutine 卡死,连接数一多直接拖垮服务。
- 读操作:起单独 goroutine 调
conn.ReadMessage,每次读前设conn.SetReadDeadline(time.Now().Add(10 * time.Second)) - 写操作:用「写队列 + 单 writer goroutine」模式,所有业务逻辑往
send chan []byte发,由 writer 统一调conn.WriteMessage - 大消息(如 base64 图片)禁用
ReadMessage,改用conn.ReadFrame+ 手动拼帧,并通过conn.SetReadLimit(1024 * 1024)限制最大帧大小
客户端连不上或秒断的常见原因
前端报 ERR_CONNECTION_REFUSED,大概率不是代码问题,而是服务根本没监听对应地址;报 i/o timeout 或 read: connection reset by peer,通常是超时配置或代理头缺失。
- 启动服务时用
http.ListenAndServe("0.0.0.0:8080", nil),别用"localhost:8080"(Docker、手机访问宿主时必踩) -
http.Server的ReadTimeout和WriteTimeout必须设为 0(禁用),WebSocket 长连接靠conn.SetReadDeadline精确控制 - 若走 Nginx,确认配置了
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade" - Chrome Network 标签页里看 ws 请求的 Request Headers 是否含
Sec-WebSocket-Key—— 没这个字段说明前端 URL 写成了http://而非ws://
CheckOrigin 和跨域必须手动处理
upgrader.CheckOrigin 默认返回 false,所有跨域请求都会被拒绝,浏览器控制台显示 WebSocket connection to 'ws://...' failed,但服务端几乎不报错,极难定位。
- 开发阶段可临时设为
func(r *http.Request) bool { return true } - 生产环境必须校验
r.Header.Get("Origin"),只放行可信域名(如https://myapp.com),不能只比对 host - 注意:如果服务部署在反向代理后,
Origin头可能被篡改或丢失,需结合X-Forwarded-For或代理白名单综合判断
真正容易被忽略的是:WebSocket 连接一旦建立,就脱离了 HTTP 生命周期管理。所有超时、心跳、错误恢复、资源清理都得自己兜底,没有“默认行为”可依赖。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











