信令交换是rtcpeerconnection连接建立的强制前置条件;漏调setlocaldescription或setremotedescription会导致流程卡在ice收集阶段,onicecandidate不触发、iceconnectionstate长期停滞,且addtrack等操作须在其后调用否则媒体流失效。

信令交换不是可选步骤,而是RTCPeerConnection连接建立的强制前置条件;漏掉setLocalDescription或setRemoteDescription任一调用,整个流程就会卡死在ICE收集阶段。
为什么setLocalDescription必须紧接createOffer之后调用
这个调用不只是“保存SDP”,它会真正激活底层ICE Agent。不调用,onicecandidate事件永远不会触发——你收不到任何候选地址,对方也永远无法连上你。
- 常见错误现象:
onicecandidate回调完全不执行,控制台无报错,但iceConnectionState长期卡在new或checking - 正确顺序必须是:
createOffer()→await setLocalDescription(offer)→ 才能开始监听候选 - 如果使用
Promise链但没await,或者用了then却忘了返回setLocalDescription的Promise,也会导致异步时序错乱
setRemoteDescription接收方必须等offer完整到达后再调用
收到的offer对象必须是完整、解析有效的SDP字符串;直接把原始event.data或未解包的JSON字段传进去,会抛出InvalidAccessError或静默失败。
- 典型错误:信令消息结构是
{ type: 'offer', sdp: 'v=0\r\n...' },但代码写成pc.setRemoteDescription(msg)而非pc.setRemoteDescription({ type: 'offer', sdp: msg.sdp }) - 务必检查
msg.sdp是否存在且非空字符串;WebSocket传输中偶尔会因分片或编码问题导致SDP截断 - 调用后应监听
pc.onnegotiationneeded或检查signalingState是否变为stable,确认状态已推进
ICE候选必须逐条发送,不能合并或延迟批量发
每个RTCIceCandidate代表一条潜在网络路径,早一秒送达,连接就可能早几百毫秒建立。合并发送(如攒够3个再发)或加防抖,会导致对方迟迟无法发起连接尝试。
- 错误做法:用
setTimeout缓存候选、或在onicecandidate里做if (candidate) { queue.push(candidate); }再统一发 - 正确做法:只要
event.candidate存在,立刻序列化为{ type: 'candidate', candidate: event.candidate.toJSON() }并发送 - 注意:
event.candidate可能是null(表示ICE收集结束),此时无需发送,也不代表错误
信令通道本身不关心内容,但必须保证消息顺序和送达
SDP和候选没有语义依赖,但有强时序依赖:offer必须在answer前到达,所有候选应在setRemoteDescription之后持续送达。TCP层保序的WebSocket基本够用,但HTTP轮询或UDP类协议容易乱序。
- Socket.io默认启用ack和重传,适合初学者;但需禁用
volatile标志,避免候选丢失 - 如果用原生WebSocket,记得监听
ws.readyState === WebSocket.OPEN再发,否则候选可能被丢弃 - 不要在信令消息里加业务字段(如
roomId)却不校验——一旦路由错位,offer发到错误客户端,双方都会卡在connecting
最容易被忽略的是:setLocalDescription和setRemoteDescription都是异步操作,但它们的完成时机直接影响后续API可用性。比如addTrack必须在setLocalDescription之后调用,否则轨道不会被纳入SDP协商。这些隐式依赖不会报错,只会让媒体流无声无影地消失。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











