必须用gorilla/websocket而非net/http原生升级,因其自动处理ping/pong、支持毫秒级writedeadline、错误堆栈可精准定位conn实例;roomid和clientid须原样透传,禁止strings.trimspace()等处理,否则导致注册失败或幽灵客户端;转发时须过滤自身消息并验证candidate字段,避免状态机崩溃。

信令服务不是媒体服务器,它只中继 offer、answer、candidate 三类 JSON 消息,不碰音视频流,也不参与 ICE 连接建立——写错逻辑或选错库,前端立刻报 Failed to set remote offer sdp: Session error code: ERROR_CONTENT 或 TypeError: Failed to execute 'addIceCandidate'。
为什么必须用 gorilla/websocket 而不是 net/http 原生升级
原生 http.Upgrade 不校验 Sec-WebSocket-Key 和协议头字段,Nginx、CDN 或某些反向代理会静默断连,且无日志提示;而 gorilla/websocket 自动处理 Ping/Pong、支持毫秒级 SetWriteDeadline、错误堆栈能精准定位到具体 conn 实例。遇到 websocket: close 1006 (abnormal closure) 时,gorilla 可查出是客户端主动断开还是代理 kill,gobwas/ws 则常只报模糊的 io.EOF。
roomid 和 clientid 必须原样透传,不能做任何字符串处理
前端发 {"cmd":"register","roomid":" abc ","clientid":"U123"},后端解析后必须原样存入 sync.Map,不能调 strings.TrimSpace()、不能转小写、不能拼接前缀。否则:
-
roomTable.Load(" abc") == nil,导致注册失败,用户反复重连却进不了房间 - 同一
clientid多次注册未先deregister,旧连接残留成“幽灵客户端”,后续消息转发触发 panic - 忘记启动
roomTable.CleanLoop()(如 AppRTC 中的go rt.cleanLoop()),超时房间堆积,新用户卡在 map 查找或锁竞争上
转发 send 消息时,如何避免前端 RTCPeerConnection 卡死
WebRTC 状态机对消息来源极其敏感,乱序、重复或发错对象会直接崩溃:
- 必须加
if clientID != msg.FromID过滤,禁止把消息回传给发送方——否则setRemoteDescription被调两次,状态非法 -
offer和answer必须严格按顺序到达,建议在消息体加"seq": 1字段,接收端丢弃seq 的包 -
candidate消息必须验证msg.Candidate字段存在且非空,空值会导致addIceCandidate报错;若含targetUserID,应只推给指定接收方,不能全房间广播 - 别用
json.Marshal+WriteMessage,改用conn.WriteJSON(msg),省去中间字节切片拷贝
并发安全和消息透传怎么不出错
裸 map[string]*websocket.Conn 并发读写会 panic;sync.Map 安全但不支持原子性“读+写”,所以:
- 房间映射键用
roomID,值用封装结构体(含conn、userID、lastSeen),不用裸指针 - 插入房间用
LoadOrStore,避免两个客户端同时加入导致重复创建或漏事件 - 广播前遍历
sync.Map.Range,每次调conn.WriteMessage后检查返回值,跳过已断开连接 - 用
json.RawMessage零拷贝透传原始字节,但接收时需确保 UTF-8 合法,否则前端解析失败
最易被忽略的是:每个 WebSocket 连接生命周期必须与 *webrtc.PeerConnection 实例严格绑定,关闭时显式调 pc.Close() 释放 UDP 端口;否则 address already in use 会持续阻塞后续连接。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











