多房间 WebSocket 必须按 room_id 分离连接集合与广播逻辑,否则导致跨房间消息泄露、并发 panic 或 goroutine 泄漏;全局 map 存所有连接会引发过滤缺失和并发写崩溃;需校验 room 参数、为每连接配专属写协程并用 channel 转发消息。

多房间 WebSocket 本质不是“加个字段就能分组”,而是必须为每个 room_id 维护独立的连接集合 + 独立的广播调度逻辑,否则必然出现跨房间消息泄露、并发 panic 或 goroutine 泄漏。
为什么不能用全局 map[*websocket.Conn]bool 存所有连接
这是最常见也最危险的误用。所有连接塞进一个 map,广播时遍历全量连接,结果 A 房间发的消息被 B 房间用户收到——因为没做 room_id 过滤。更严重的是:map 本身不支持并发写入,conn.WriteMessage() 调用又非并发安全,多个 goroutine 同时写同一连接会直接 panic: concurrent write to websocket connection。
- 必须按
room_id拆分数据结构,例如rooms map[string]*Room -
*Room内部用sync.Map[*websocket.Conn]struct{}或带sync.RWMutex的切片,避免fatal error: concurrent map writes - URL 中的
room参数必须校验:只允许[a-zA-Z0-9_-],长度限制(如 ≤32),拒绝空值或超长值
每个连接必须配专属写协程,不能在广播循环里直接调 WriteMessage
广播时若对每个 *websocket.Conn 直接调 WriteMessage(),一旦某个连接卡住(弱网、假死),整个广播循环就 hang 住,后续所有房间消息积压;同时,读协程可能正在处理心跳响应,与写冲突触发 panic。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 每个连接初始化时启动一个写协程:
go func() { for msg := range conn.writeCh { conn.SetWriteDeadline(time.Now().Add(3 * time.Second)); conn.WriteMessage(...) } }() - 房间广播只做转发:
for conn := range room.clients { select { case conn.writeCh - 连接关闭时务必
close(conn.writeCh)并让写协程自然退出,否则 goroutine 和 channel 都泄漏
房间怎么自动清理?别在 Read/Write 协程里删房间
房间清理由 Hub 或 Room 自身的监控 goroutine 统一处理,不能分散在每个连接的 Read/Write 协程里——否则多个协程同时判断“是否只剩我一个”,可能竞态导致房间残留或误删。
- 连接断开时,通过
unregisterchannel 通知 Hub:“我要离开房间” - Hub 在处理
unregister后,检查该房间的clients是否为空;为空则从roomsmap 中删除,并触发room.Close()(清理内部 channel、stop 心跳等) - 不要依赖
time.AfterFunc定时扫描——延迟高且浪费 CPU;应基于连接数变化实时触发
前端传的 room/user 字段怎么防伪造
WebSocket 是长连接,但首次握手后没有任何身份保障。URL query 中的 room=xxx、消息体里的 {"user":"admin"} 全不可信,必须服务端强校验。
- 连接建立后,第一帧必须是认证消息(如 JWT 或 session ID),服务端验证通过才允许加入房间
- room_id 不由前端生成,而由服务端分配(如登录后下发);若必须前端传,需签名校验(HMAC-SHA256 + timestamp)
- 所有广播消息中注入的
room、user_id字段,必须由服务端写入,禁止透传客户端原始字段 - 避免用 Cookie 传 room_id:跨域 WebSocket 默认不带 Cookie,移动端 WebView 行为不一致
真正难的不是“怎么存连接”,而是如何让每个房间的生命周期、每个连接的读写、每条消息的路由和清理全部串成一条无竞争、无泄漏、无超时阻塞的链路——任何一环松动,线上就会表现为消息乱序、连接堆积或内存缓慢上涨。

















