7、端到端连接建立:Offer/Answer流程、ICE候选者收集、连接状态机、重连机制
说实话,WebRTC 最核心的魅力,就是让两个浏览器能直接“对话”。但这个过程远没有想象中那么简单。你想想看,两个设备可能在不同的局域网后面,中间隔着 NAT、防火墙,甚至还有各种奇怪的代理。怎么找到对方?怎么建立连接?
这一章,我们就来拆解这个“握手”过程。我会结合我自己的踩坑经历,把 Offer/Answer、ICE、状态机、重连这些概念讲透。
7.1 Offer/Answer 流程:协商的起点
Offer/Answer,说白了就是“你提需求,我回应”。这是 WebRTC 连接建立的第一个步骤。没有这个协商,后面的一切都无从谈起。
流程其实很清晰:
- 发起方(Offerer) 创建一个 Offer,里面包含它支持的媒体能力(比如编解码器、分辨率、带宽等)。
- Offer 通过信令通道(比如 WebSocket)发送给 应答方(Answerer)。
- 应答方收到 Offer 后,创建一个 Answer,表示“我接受这些能力,或者我调整一下”。
- Answer 再通过信令通道返回给发起方。
嗯,这里要注意:Offer 和 Answer 都是 SDP(Session Description Protocol)格式的文本。SDP 看起来像配置文件,里面一行一行地描述了媒体信息。
核心要点: Offer/Answer 只是“能力协商”,不是真正的媒体数据传输。它只是告诉对方“我能做什么,你看着办”。
我在项目中遇到过一个问题:有一次,Offer 里声明了 H.264 编码,但 Answer 方只支持 VP8。结果连接建立后,视频一直黑屏。后来我才意识到,SDP 协商失败时,WebRTC 不会自动降级,而是直接报错。所以,一定要在业务层做好编解码器的兼容性检查。
7.2 ICE 候选者收集:找到对方的路
Offer/Answer 协商完能力后,接下来就是“找路”了。ICE(Interactive Connectivity Establishment)就是干这个的。
ICE 的核心思想是:收集所有可能的连接路径(候选者),然后尝试连接,直到成功。
候选者分为三种类型:
| 类型 | 说明 | 优先级 |
|---|---|---|
| host | 本机 IP 地址(比如 192.168.1.100) | 最高 |
| srflx | 经过 NAT 映射后的公网 IP(通过 STUN 获取) | 中等 |
| relay | 通过 TURN 服务器中转的地址 | 最低 |
为什么会有这么多类型?因为网络环境太复杂了。两个设备在同一个局域网时,直接用 host 候选者就能连上。但如果一个在 NAT 后面,另一个也在 NAT 后面,就需要 srflx 甚至 relay。
我个人习惯在调试时,先打印出所有候选者,看看有没有漏掉关键的地址。我曾经遇到过一个案例:客户反馈视频通话经常卡顿,排查后发现是 TURN 服务器带宽不够,导致 relay 候选者延迟很高。后来我们优化了 TURN 服务器的部署,问题才解决。
小技巧: 在浏览器中,可以通过 RTCPeerConnection 的 onicecandidate 事件来监听候选者的收集过程。每个候选者都会触发一次回调。
7.3 连接状态机:从“等待”到“连接”
WebRTC 的连接状态不是一成不变的。它有一个清晰的状态机,用来描述连接的当前阶段。
状态机的主要状态如下:
- new:刚创建 PeerConnection,还没开始连接。
- checking:正在检查候选者,尝试建立连接。
- connected:至少有一条候选者路径成功建立了连接。
- completed:所有候选者都检查完毕,连接稳定。
- failed:所有候选者都失败了,连接无法建立。
- disconnected:连接中断,但可能还能恢复。
- closed:连接被主动关闭。
为什么会这样设计?因为网络是动态的。比如,你从 Wi-Fi 切换到 4G 时,IP 地址变了,原来的连接就会断开。WebRTC 的状态机就是为了应对这种变化。
我记得有一次,用户反馈视频通话突然中断,但几秒后又自动恢复了。查看日志发现,状态机从 connected 跳到了 disconnected,然后又回到了 connected。这是因为网络短暂波动,ICE 重新协商后找到了新的路径。
注意: 不要依赖 connected 状态来判断“连接是否可用”。因为 connected 只表示“曾经连上过”,不代表当前还能传输数据。建议结合 iceConnectionState 和 connectionState 一起判断。
7.4 重连机制:网络波动也不怕
网络不可能永远稳定。Wi-Fi 信号弱、4G 切换、路由器重启……这些都会导致连接中断。WebRTC 内置了重连机制,但默认行为可能不够智能。
重连的核心逻辑是:
- 检测到连接断开(比如
iceConnectionState变为disconnected)。 - 触发 ICE 重新收集候选者(Restart ICE)。
- 重新执行 Offer/Answer 流程,协商新的连接参数。
- 尝试新的候选者路径,直到连接恢复。
但这里有个坑:默认的 ICE Restart 是自动触发的,但触发条件比较保守。有时候网络已经断了,但 WebRTC 还在傻等,导致用户感知到明显的卡顿。
我曾经在项目中做过一个优化:主动检测网络状态。比如,通过 WebSocket 的心跳包来判断网络是否正常。如果心跳超时,就主动调用 createOffer() 并设置 iceRestart: true,强制触发重连。这样,重连的响应速度比默认快了好几秒。
避坑指南: 我曾经在重连时忘记更新 SDP 中的 ICE 凭证,导致重连后一直认证失败。后来我养成了一个习惯:每次重连时,都重新生成 ICE 凭证,并确保信令通道能正确传递新的凭证。
7.5 知识体系总览
为了让你更直观地理解整个流程,我画了一张图。这张图展示了从 Offer/Answer 到 ICE 候选者收集,再到连接建立和重连的完整链路。
这张图把整个流程分成了三个阶段。第一阶段是 Offer/Answer 协商,第二阶段是 ICE 候选者收集,第三阶段是连接建立和状态机管理。每个阶段都有子步骤,你可以对照着看。
好了,这一章的内容就到这里。端到端连接建立是 WebRTC 最核心的环节,也是最容易出问题的地方。理解了 Offer/Answer、ICE、状态机和重连机制,你就能应对大部分网络场景了。