WebRTC 的 ICE candidate 协商
文章目录
- 前言
- WebRTC 的 ICE candidate 协商
- 1. 什么是 ICE candidate?
- 2. ICE 协商的流程
- 3.前端使用 ICE candidate 协商代码示例
- 1)收集 candidate 并发送
- 2)WebSocket 接收 candidate 并添加
- 4. ICE candidate 的类型
- 5. ICE 协商常见问题
- 6. 关键调试点
- 7. 代码片段加日志示例
- 总结
前言
WebRTC 的 ICE candidate 协商
WebRTC 的 ICE candidate 协商
1. 什么是 ICE candidate?
- ICE(Interactive Connectivity Establishment) 是 WebRTC 用于 NAT 穿透、建立点对点连接的核心协议。
- candidate(候选地址)是指本地网络环境下所有可能的可用地址(如本地IP、公网IP、中继服务器IP等)。
- 通过收集和交换 candidate,WebRTC 能找到一条可用的网络路径,实现端到端音视频传输。
2. ICE 协商的流程
- 双方创建 RTCPeerConnection,并设置 ICE 服务器(通常是 STUN/TURN)。
- 本地收集 candidate:浏览器会自动收集本地所有可用的网络地址(candidate)。
- onicecandidate 事件触发:每收集到一个 candidate,就通过信令服务器发送给对端。
- 对端收到 candidate,通过
addIceCandidate
添加到自己的 RTCPeerConnection。 - 双方不断交换 candidate,直到找到一条可用的网络路径,ICE 连接建立,媒体流就能传输了。
3.前端使用 ICE candidate 协商代码示例
1)收集 candidate 并发送
const pc = new RTCPeerConnection({iceServers: [// 免费的公共 STUN 服务,适用于测试和轻量级应用{ urls: "stun:stun.l.google.com:19302" },],})
pc.onicecandidate = (e) => {if (e.candidate) {ws.send(JSON.stringify({ type: "candidate", candidate: e.candidate }))}
}
-
每当本地收集到一个 candidate,就通过 WebSocket 发送给对方。
RTCPeerConnection ,截图自MDN。
2)WebSocket 接收 candidate 并添加
前端可以用 ws 这个库。
ws.onmessage = async (msg) => {if (data.type === "candidate") {try {await pc.addIceCandidate(data.candidate)} catch (e) {}}}
- 收到对方的 candidate 后,调用
addIceCandidate
添加到本地 RTCPeerConnection。
4. ICE candidate 的类型
- host:本地局域网 IP 地址
- srflx:服务器反射地址(通过 STUN 服务器获取的公网 IP)
- relay:中继地址(通过 TURN 服务器获取,适合复杂 NAT/防火墙环境)
5. ICE 协商常见问题
- 没有 candidate 交换:双方无法建立连接,媒体流无法传输。
- addIceCandidate 报错:SDP 协商顺序不对,或 candidate 格式有误。
- NAT/防火墙阻断:需要配置 TURN 服务器。
- 局域网可通,公网不通:STUN 只能穿透部分 NAT,复杂网络需 TURN。
6. 关键调试点
- 在
onicecandidate
、addIceCandidate
前后加日志,确认 candidate 是否收发完整。 - 检查 candidate 的类型和内容,确认是否有
srflx
或relay
类型。 - 浏览器控制台查看 ICE 相关报错(如 ICE failed、addIceCandidate failed 等)。
7. 代码片段加日志示例
pc.onicecandidate = (e) => {if (e.candidate) {console.log('Send ICE candidate:', e.candidate);ws.send(JSON.stringify({ type: "candidate", candidate: e.candidate }))}
}else if (data.type === "candidate") {console.log('Receive ICE candidate:', data.candidate);try {await pc.addIceCandidate(data.candidate)} catch (e) {console.error('addIceCandidate error', e);}
}
总结
- ICE candidate 协商是 WebRTC 能否连通的关键,决定了媒体流能否端到端传输。