必须用gorilla/websocket而非net/http升级,因其校验Sec-WebSocket-Key、自动处理Ping/Pong、毫秒级超时控制及精准错误定位;roomid和clientid须原样透传,禁止字符串清洗,否则导致注册失败或幽灵连接。

Go 微服务不能当 WebRTC 媒体服务器用,但可以稳稳当当做信令网关——前提是别把它当成“能推流、能解码、能渲染”的全栈组件。
为什么信令网关必须用 gorilla/websocket 而不是 net/http 升级
原生 http.Upgrade 不校验 Sec-WebSocket-Key 和协议头,Nginx 或 CDN 会静默断连,且无日志提示;gorilla/websocket 自动处理 Ping/Pong、支持毫秒级 SetWriteDeadline,出错时堆栈能精准定位到具体 conn 实例。遇到 websocket: close 1006 (abnormal closure),用 gorilla 才能区分是客户端主动断开,还是反向代理 kill 掉的连接。
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 查找或锁竞争上
转发 offer/answer/candidate 时,状态机极易崩溃
WebRTC 状态机对消息来源极其敏感,乱序、重复或发错对象会直接崩溃:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 必须加
if clientID != msg.FromID过滤,禁止把消息回传给发送方——否则setRemoteDescription被调两次,状态非法 -
offer和answer必须严格按顺序到达,建议在消息体加"seq": 1字段,接收端丢弃seq 的重复消息 - ICE candidate 必须逐条发送(不能合并进
offer),且要在pc.OnICECandidate回调里触发发送,否则对方收不到
pion/webrtc 的 PeerConnection 实例不能复用或共享配置
每个 WebSocket 连接必须绑定唯一 *webrtc.PeerConnection 实例,生命周期与连接严格一致:
- 别复用
*webrtc.Configuration里的SettingEngine或MediaEngine到多个pc,它们不是线程安全的共享对象 - 每个
pc必须有自己的OnICECandidate回调,里面把 candidate 推给对应客户端(不能广播) - 关闭连接时,必须显式调用
pc.Close(),否则 UDP 端口不释放,net.ListenUDP可能报address already in use
真正容易被忽略的是:信令网关从不参与 ICE 连接建立,也不碰音视频流——它只中继三类 JSON 消息:offer、answer、candidate。写错逻辑或选错库,前端立刻报 Failed to set remote offer sdp: Session error code: ERROR_CONTENT 或 TypeError: Failed to execute 'addIceCandidate',而错误日志里往往只显示“协商失败”,没提到底是哪条 candidate 格式不对、哪个 seq 乱了、还是哪个 roomid 被 trim 掉了空格。

















