ICE 在 WebRTC 中的角色与工作原理(深入指南)
本文面向实时音视频与前端工程师,系统讲解 ICE(Interactive Connectivity Establishment)在 WebRTC 中的职责、候选收集与连通性检查流程、提名与选路、Trickle ICE 与 ICE 重启、ICE-lite 等实践要点。含 Mermaid 图示与示例代码(需支持 Mermaid 渲染)。
整体概览:ICE 是"找路并选路"的框架
- 职责:统一候选收集(host/srflx/relay)、构建候选对、进行 STUN 连通性检查、提名最佳路径、监控并维护连接。
- 目标:在复杂网络/NAT/防火墙环境下,尽可能选择延迟低、稳定性高、成本低的路径传输媒体与数据。
- 与 STUN/TURN 的关系:
- STUN 提供
srflx候选与连通性检查消息。 - TURN 提供
relay候选作为兜底中继。 - ICE 把二者串起来决定最终的传输路径。
- STUN 提供
候选类型与来源
host:本机地址候选(现代浏览器通常以 mDNS 名称暴露,保护隐私)。srflx(server-reflexive):通过 STUN 获取的公网映射地址(家庭 NAT 下常见且可直连)。relay:由 TURN 提供的中继地址(对称 NAT、企业防火墙兜底)。- 采集策略:浏览器在
RTCPeerConnection初始化后根据iceServers配置并行收集,Trickle ICE 可边收集边上报。
Browser STUN Server TURN Server Signal ICE 候选收集开始 生成 host 候选(mDNS) STUN Binding Request XOR-MAPPED-ADDRESS (srflx) TURN Allocate Relayed Address (relay) Trickle ICE 上报候选 Browser STUN Server TURN Server Signal
候选配对、优先级与连通性检查
- 候选对(Candidate Pair):将本地与远端候选组合成若干"对",每个对进行 STUN 探测。
- 优先级:依据类型(host > srflx > relay)、成本、预估路径质量等计算,ICE 会优先检查高优对以加快建链。
- 检查过程:
- 通过 STUN 发送请求并等待响应,记录往返与可达性。
- 控制角色(
ICE-CONTROLLING)在成功的候选对上发送USE-CANDIDATE进行"提名(Nomination)"。 - 一旦提名成功,双方将该路径确认为最终传输流的
nominatedpair。
Peer A (Controlling) Peer B (Controlled) STUN Check STUN Success STUN Check STUN Success (nominated) 选路完成,后续 DTLS 握手与 SRTP 传输 Peer A (Controlling) Peer B (Controlled)
Offer/Answer、Trickle ICE 与 ICE 重启
- Offer/Answer:通过信令交换 SDP,包含 ICE 参数(
a=ice-ufrag/a=ice-pwd/a=candidate)。 - Trickle ICE:候选边收集边发送,缩短建链时延;需要
a=ice-options:trickle支持。 - ICE 重启:网络切换或连接质量下降时可触发重启,使用
pc.createOffer({ iceRestart: true })生成新的 ICE 参数并重新检查。
Peer A Peer B Signal createOffer (含部分候选) setLocalDescription + 发送 Offer setRemoteDescription(Offer) createAnswer setLocalDescription + 发送 Answer 追加候选 addIceCandidate 追加候选 addIceCandidate STUN Checks STUN Responses par [Trickle ICE] [连通性检查并提名] Peer A Peer B Signal
ICE 角色与模式:Controlling/Controlled、ICE-lite
- 角色:
ICE-CONTROLLING(通常为发起 Offer 的一方)负责最终提名。ICE-CONTROLLED(Answer 方)配合检查与确认提名。
- ICE-lite:
- 服务器侧的精简模式,通常不进行复杂的候选收集与检查,依赖对端进行决策。
- 典型于媒体服务器或网关;浏览器端为 ICE-full。
浏览器端实践:配置、Trickle 与重启
- 基本配置:
js
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
// 若需兜底,提供可靠 TURN(TLS/443)
{ urls: 'turns:turn.example.com:443?transport=tcp', username: 'user', credential: 'pass' }
],
// 根据场景调整,默认 0 即不预收集
iceCandidatePoolSize: 0,
// 可选策略:'all' 优先直连,'relay' 强制走 TURN
// iceTransportPolicy: 'all'
});
pc.onicecandidate = ({ candidate }) => {
if (candidate) {
sendSignal({ type: 'candidate', candidate });
} else {
// end-of-candidates
sendSignal({ type: 'eoc' });
}
};
pc.onicegatheringstatechange = () => {
console.log('gathering:', pc.iceGatheringState);
};
pc.oniceconnectionstatechange = () => {
console.log('connection:', pc.iceConnectionState);
};
// ICE 重启(网络切换/弱网恢复)
async function iceRestart() {
const offer = await pc.createOffer({ iceRestart: true });
await pc.setLocalDescription(offer);
sendSignal({ type: 'offer', sdp: offer.sdp });
}
- Trickle ICE 处理:
js
// 远端逐步发送过来的候选
async function onRemoteCandidate(c) {
await pc.addIceCandidate(c);
}
// 接收到对端 EOC(end-of-candidates)时,可视情况停止等待
function onEndOfCandidates() {
console.log('remote end-of-candidates');
}
SDP 与 ICE 关键字段(示意)
sdp
v=0
o=- 46117326 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=ice-options:trickle
m=audio 9 UDP/TLS/RTP/SAVPF 111 0
c=IN IP4 0.0.0.0
a=mid:0
a=sendrecv
a=rtpmap:111 opus/48000/2
a=setup:actpass
a=fingerprint:sha-256 3A:...:7F
# ICE 凭据,用于 STUN 检查完整性与鉴权
a=ice-ufrag:u1Ab
a=ice-pwd:9kD3...
# 典型候选(示意)
a=candidate:1 1 udp 2122260223 192.168.1.5 54321 typ host
a=candidate:2 1 udp 1686052607 203.0.113.10 62000 typ srflx raddr 192.168.1.5 rport 54321
a=candidate:3 1 udp 1518280447 203.0.113.20 3478 typ relay
常见问题与排障
- 无候选或仅
host:- 检查 STUN 服务器可达性与端口(UDP 3478);更换公共 STUN 测试。
- 确认信令是否正确传递 ICE 参数与候选(包括 Trickle 流的
addIceCandidate)。
- 一直走
relay:- 网络严格或对称 NAT;直连不可达时属正常;确保 TURN over TLS/443 配置可靠。
- 建链慢或失败:
- 适当开启 Trickle ICE;减少候选数量;优化优先级;检查双方时间顺序(先
setLocalDescription再发送)。
- 适当开启 Trickle ICE;减少候选数量;优化优先级;检查双方时间顺序(先
- 切网后断流:
- 触发 ICE 重启;在弱网场景适当调整码率与分辨率。
- 隐私与安全:
- 浏览器使用 mDNS 隐藏内网 IP;媒体走 DTLS-SRTP 加密;TURN 层不能解密 SRTP。
实操工具与建议
- 使用本仓库测试页
stun-test.html验证候选收集与 STUN/TURN 可达性(需本地服务器访问)。 - 客户端策略:保留直连为首选,提供可靠 TURN 兜底;必要时将
iceTransportPolicy: 'relay'强制中继。 - 服务端策略:部署高质量 TURN(coturn),支持 UDP/TCP/TLS,提供临时凭证,监控带宽与并发。
术语速查
- 候选(Candidate):潜在的传输地址与端口组合(host/srflx/relay)。
- 候选对(Candidate Pair):本地与远端候选的组合,进行检查与选路的基本单位。
- 提名(Nomination):选择最终用于传输的候选对过程(
USE-CANDIDATE)。 - Trickle ICE:候选边收集边发送,缩短建链时延。
- ICE 重启:网络变化时重新探测的流程(
iceRestart)。 - ICE-lite:精简模式,多见于服务器侧。
参考与延伸阅读
- RFC 8445: Interactive Connectivity Establishment (ICE)
- RFC 5389: Session Traversal Utilities for NAT (STUN)
- RFC 5766: Traversal Using Relays around NAT (TURN)
- JSEP/MDN:
RTCPeerConnection,iceServers,onicecandidate,iceTransportPolicy
总结:ICE 是 WebRTC 的"连通性管家"。它通过候选收集与 STUN 检查,自动选择最优路径,并在必要时回退到 TURN 中继。结合 Trickle ICE 与 ICE 重启策略、合理的 STUN/TURN 部署与信令实现,就能在复杂网络中保持稳定、低延迟的实时传输体验。