漏掉setlocaldescription会导致ice收集卡死、onicecandidate永不触发;必须按createoffer→await setlocaldescription→监听候选的顺序执行,且setremotedescription需传完整sdp对象、候选须逐条实时发送。

漏掉 setLocalDescription 就卡死在 ICE 收集阶段
不调用 setLocalDescription,onicecandidate 事件永远不会触发——你收不到任何候选地址,对方也连不上你。这个调用不是“存个 SDP”,而是真正激活底层 ICE Agent。
常见错误现象:onicecandidate 完全不执行,控制台无报错,但 iceConnectionState 长期卡在 new 或 checking。
- 正确顺序必须是:
createOffer()→await setLocalDescription(offer)→ 才能开始监听候选 - 如果用
.then()链但没返回setLocalDescription的 Promise,异步时序会错乱 - 用
async/await最稳妥,别写成pc.setLocalDescription(offer).then(...)却忘了return
setRemoteDescription 接收方必须传完整 SDP 对象
直接把原始 WebSocket 消息或未解包的 JSON 字段传给 setRemoteDescription,会抛 InvalidAccessError 或静默失败。
典型错误:信令消息结构是 { type: 'offer', sdp: 'v=0\r\n...' },但代码写成 pc.setRemoteDescription(msg),而非 pc.setRemoteDescription({ type: msg.type, sdp: msg.sdp })。
- 务必检查
msg.sdp是否存在且非空字符串;WebSocket 传输中偶有分片或编码导致 SDP 截断 - 调用后应监听
pc.signalingState是否变为"stable",或观察是否触发onnegotiationneeded - 若收到
answer后仍卡住,大概率是setRemoteDescription调用失败但没捕获异常
ICE 候选必须逐条、立刻发送,不能攒批
每个 RTCIceCandidate 代表一条潜在网络路径,早一秒送达,连接就可能早几百毫秒建立。合并发送(如攒够 3 个再发)或加防抖,会导致对方迟迟无法发起连接尝试。
- 正确做法:只要
event.candidate存在,立刻序列化为{ type: 'candidate', candidate: event.candidate.toJSON() }并发送 -
event.candidate可能为null(表示 ICE 收集结束),此时无需发送,也不代表错误 - 错误做法:在
onicecandidate里做if (candidate) { queue.push(candidate); }再统一发
信令通道本身不关心内容,但强依赖顺序和送达
SDP 和候选没有语义依赖,但有强时序依赖:offer 必须在 answer 前到达,所有 candidate 必须在对应 setRemoteDescription 后送达——哪怕晚 100ms,都可能导致连接失败。
- WebSocket 或 Socket.io 必须保证消息按发送顺序抵达;HTTP 轮询容易乱序,不推荐用于生产
- 房间逻辑要明确:用
roomId或targetId控制广播范围,避免 offer 发错人 - 前端发信令前可加简单校验,比如
if (!msg.sdp || !msg.type) return;,避免无效消息污染流程
signalingState、iceConnectionState、iceGatheringState)和错误捕获——它们不会自动报错,但会静默卡住整个连接。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











