【音视频】WebRTC ICE 模块深度剖析
原文链接:
https://mp.weixin.qq.com/s?__biz=MzIzMjY3MjYyOA==&mid=2247498075&idx=2&sn=6021a2f60b1e7c71ce4d7af6df0b9b89&chksm=e893e540dfe46c56323322e780d41aec1f851925cfce8b76b3f4d5cfddaa9c7cbb03a7ae4c25&scene=178&cur_album_id=3146991347015499781#rd
一、WebRTC ICE
1.1 ICE 是什么?
在 WebRTC(网页实时通信)技术体系中,ICE(Interactive Connectivity Establishment,交互式连接建立) 是解决 “设备间如何突破网络壁垒(如 NAT 网关)建立直接通信” 的核心协议。它通过整合 STUN(会话穿越实用工具) 和 TURN(中继穿越实用工具),动态收集通信候选地址、检查连通性,并最终选择最优路径(优先 P2P,失败时降级为中继),确保实时音视频、数据传输的可靠性
1.2 为什么需要 ICE?
WebRTC 的目标是实现 “浏览器 / 终端设备间的直接通信(P2P)”,但现实网络中存在两大障碍:
- IP 地址短缺:IPv4 地址耗尽导致绝大多数设备处于局域网内,仅拥有内网 IP(如 192.168.x.x、10.x.x.x),无法直接被公网设备访问。
- NAT 网关隔离:NAT(网络地址转换)是解决 IP 短缺的核心技术,但它会 “隐藏” 内网设备的真实地址,仅允许内网设备主动发起的外网连接,拒绝外部设备的主动访问 —— 这直接阻断了 P2P 通信的可能性。
ICE 的核心思想正是绕开 NAT 限制:通过收集设备的 “通信候选地址(Candidate)”,尝试不同路径的连通性,最终找到一条能让两端互通的路径。
1.3 通信候选地址(Candidate)
ICE 为每个设备(称为 “代理”)收集多种 Candidate 地址(IP + 端口 + 传输协议的组合,WebRTC 中默认用 UDP),每种 Candidate 对应一种潜在的通信路径。核心类型如下:
Candidate 类型 | 来源与作用 | 适用场景 |
---|---|---|
Host Candidate(本地候选) | 设备本地网卡的真实地址(如内网 IP 或公网 IP) | 两端处于同一局域网(如办公室内设备),或一端拥有公网 IP |
Srflx Candidate(服务器反射候选) | 设备向 STUN 服务器发送请求,STUN 返回 NAT 网关的 “公网映射地址”(内网 IP 经 NAT 转换后的公网地址) | 两端均通过 圆锥型 NAT 接入公网,可通过反射地址实现 P2P 打洞 |
Prflx Candidate(对等反射候选) | 连通性检查过程中,通过对端的 STUN Binding 请求 “动态发现” 的 NAT 反射地址 | 补充 Srflx 覆盖不到的场景(如对称型 NAT 下的临时映射地址) |
Relay Candidate(中继候选) | 设备向 TURN 服务器请求,由 TURN 分配的 “中继地址”(所有数据通过 TURN 服务器转发) | P2P 打洞失败时(如对称型 NAT 间通信),降级为中继传输 |
关键区分:Srflx 与 Prflx |
- Srflx 是通过 STUN 服务器主动获取的,提前通过信令交换给对端;
- Prflx 是在 连通性检查阶段动态发现的,无需提前交换,仅在检查过程中生成。
1.4 STUN 与 TURN 协议
ICE 本身不直接 “穿越 NAT”,而是依赖 STUN 和 TURN 实现核心能力。二者分工明确:STUN 负责 “发现 NAT 映射地址” 和 “检查连通性”,TURN 负责 “P2P 失败时提供中继”。
STUN:NAT 地址发现与连通性检查
STUN(Session Traversal Utilities for NAT)是一种轻量级协议,主要解决两个问题:
- 获取 NAT 公网映射地址:内网设备向 STUN 服务器发送
Binding Request
,STUN 服务器返回设备的 “公网映射地址(MAPPED-ADDRESS)”,设备据此生成 Srflx Candidate。 - 验证连通性:ICE 用 STUN 的
Binding Request/Response
检查 “候选地址对(Candidate Pair)” 是否能互通(即 “打洞” 是否成功)。
(1)STUN 的版本演进:RFC3489 vs RFC5389
ICE 仅支持 RFC5389(现代 STUN),因为早期的 RFC3489(Classic STUN)存在诸多局限性:
特性 | RFC3489(Classic STUN) | RFC5389(现代 STUN) |
---|---|---|
传输协议支持 | 仅 UDP | 支持 UDP/TCP/TLS |
安全认证 | 无 | 支持短期 / 长期凭证认证 |
NAT 类型覆盖 | 不支持对称型 NAT | 支持对称型 NAT 检测 |
IPv6 支持 | 无 | 支持 IPv6 |
地址篡改防护 | 无 | 提供 XOR-MAPPED-ADDRESS |
(2)STUN 消息格式:20 字节头 + 可变属性
STUN 消息由 “固定头部” 和 “可变属性” 组成,结构如下:
-
关键头部字段:
- 魔术字(Magic Cookie):固定值
0x2112A442
,用于区分 STUN 消息与其他协议(如 RTP),并参与 XOR 地址计算。 - 事务 ID(Transaction ID):96 位随机值,用于关联 “请求(Request)” 与 “响应(Response)”(避免多请求时混淆)。
- 魔术字(Magic Cookie):固定值
ICE 核心扩展属性(STUN 为 ICE 新增的关键属性):
属性名 | 作用 |
---|---|
PRIORITY | 标识 Candidate 的优先级,用于计算 Candidate Pair 的优先级(决定检查顺序) |
USE-CANDIDATE | 提名 Candidate Pair 为 “可用路径”,触发后续 DTLS 加密连接建立 |
ICE-CONTROLLING | 标识当前设备为 “控制角色(Controlling)”,携带 Tie-breaker 解决角色冲突 |
ICE-CONTROLLED | 标识当前设备为 “被控角色(Controlled)”,携带 Tie-breaker 解决角色冲突 |
XOR-MAPPED-ADDRESS | 对映射地址进行 XOR 加密,防止 NAT 网关篡改地址(修复 ALG 设备的兼容性) |
MESSAGE-INTEGRITY | 基于 HMAC-SHA1 的消息完整性校验(用 ICE 协商的 ice-pwd 计算) |
FINGERPRINT | CRC32 指纹校验,进一步区分 STUN 消息与其他协议(如 RTP/RTCP) |
TURN:P2P 失败时的中继方案
当 STUN 无法打通 NAT(如两端均为对称型 NAT)时,ICE 会降级为 TURN(Traversal Using Relays around NAT) 中继传输。TURN 本质是一个 “中继服务器”,核心作用是:
- 为设备分配 Relay Candidate(中继地址);
- 转发两端的音视频数据(所有流量经 TURN 服务器中转)。
TURN 的关键特性:
- 协议兼容性:基于 STUN 扩展(除
ChannelData
消息外,其他消息格式与 STUN 一致),支持 UDP/TCP/TLS; - 权限控制:设备需先向 TURN 服务器发送
Allocate
请求 “申请中继资源”,并创建 Permission(许可)—— 仅允许被许可的对端地址通过 TURN 转发数据; - 效率优化:支持
ChannelData
消息(无 STUN 头部,减少开销),适合大流量传输。
1.5 ICE 细节
NAT 类型
NAT 网关的转发规则直接决定 P2P 能否成功,ICE 需先检测 NAT 类型,再选择合适的 Candidate。常见 NAT 类型分为 圆锥型 NAT 和 对称型 NAT,其中圆锥型又细分为三类:
NAT 类型 | 核心规则 | P2P 兼容性 |
---|---|---|
完全圆锥型 NAT | 内网 IP:Port 映射为固定公网 IP:Port,任何外网设备可向该公网地址发送数据 | 最佳(支持所有 P2P 场景) |
IP 限制圆锥型 NAT | 内网 IP:Port 仅向 “主动访问过的外网 IP” 开放(端口不限) | 较好(仅限制 IP,支持大部分场景) |
Port 限制圆锥型 NAT | 内网 IP:Port 仅向 “主动访问过的外网 IP:Port” 开放(IP + 端口双重限制) | 一般(需精准匹配端口) |
对称型 NAT | 内网 IP:Port 向不同外网 IP:Port 发起请求时,映射不同的公网 IP:Port | 最差(P2P 几乎无法打通,需 TURN 中继) |
NAT 类型检测流程:
ICE 代理通过向 STUN 服务器发送多轮请求,按以下顺序判断 NAT 类型:
- 检查防火墙是否阻断所有 UDP 包(若无法收到 STUN 响应,判定为 “UDP 阻断”);
- 检查设备是否拥有公网 IP(若本地 IP 与 STUN 返回的映射地址一致,判定为 “公网设备”);
- 检测是否为 “完全圆锥型 NAT”(向不同 STUN 服务器发送请求,若映射地址不变,且外部可主动访问);
- 检测是否为 “对称型 NAT”(向同一 STUN 服务器的不同端口发送请求,若映射地址变化,判定为对称型);
- 区分 “IP 限制” 与 “Port 限制圆锥型”(向同一 IP 的不同端口发送请求,若能收到响应则为 IP 限制,否则为 Port 限制)。
具体能否打通可以看下表:
ICE协议包括stun和turn协议,turn协议是stun协议的补充,可以简单粗暴理解为如果stun不通,那就走turn,turn可以理解为一个中继代理转发。
ICE 的角色与模式
ICE 定义了 “角色” 和 “模式”,用于规范两端的行为逻辑,避免冲突。
(1)ICE 角色:Controlling vs Controlled
ICE 两端分为两种角色,决定 “谁负责提名可用路径”:
- Controlling(控制角色):主动提名 “可用的 Candidate Pair”,由 Offer 方(主动发起会话的设备)担任;
- Controlled(被控角色):被动接受 Controlling 提名的路径,由 Answer 方(被动响应会话的设备)担任。
角色冲突解决:
若两端均误判为 Controlling(或 Controlled),通过 Tie-breaker(随机值) 解决:
- 冲突时,两端在 STUN Binding 请求中携带
ICE-CONTROLLING
/ICE-CONTROLLED
属性及 Tie-breaker 值; - 比较 Tie-breaker 大小:值更大的一端为 Controlling,另一端需切换为 Controlled;
- 若某端需切换角色,对端会返回
487(Role Conflict)
错误,触发角色变更。
ICE 模式:Full ICE vs Lite ICE
ICE 代理分为两种模式,对应不同的部署场景:
模式 | 核心行为 | 适用场景 |
---|---|---|
Full ICE | 主动收集所有 Candidate,主动发起连通性检查,支持角色切换 | 客户端设备(如浏览器、APP) |
Lite ICE | 仅被动响应连通性检查(不主动发起),角色固定为 Controlled,SDP 含 a=ice-lite | 公网服务器(如媒体服务器、CDN 节点) |
注意:Full ICE 与 Lite ICE 互通时,仅 Full ICE 端主动发起检查,Lite 端仅需回应即可。
ICE 的完整工作流程
ICE 从 “收集 Candidate” 到 “建立通信” 分为 8 个核心步骤,流程如下:
步骤 1:收集 Candidate(Candidate Collection)
ICE 代理从本地、STUN、TURN 收集所有可能的 Candidate,并去重(若两个 Candidate 的地址和 Base 地址相同,则删除重复项):
- Host Candidate:遍历本地网卡(物理 / 虚拟),获取所有内网 / 公网 IP:Port;
- Srflx Candidate:向 STUN 服务器发送
Binding Request
,获取 NAT 公网映射地址; - Relay Candidate:向 TURN 服务器发送
Allocate
请求,获取中继地址; - Foundation 生成:为每个 Candidate 生成唯一标识(基于类型、IP、协议计算),用于后续去重。
步骤 2:交换 Candidate(Candidate Exchange)
两端通过 信令服务器 交换 Candidate(需与 SDP 协商同步),有两种交换方式:
- 传统方式:收集完所有 Candidate 后,将其嵌入 SDP 中一次性发送;
- Trickle ICE(涓流 ICE):SDP 与 Candidate 分开发送(SDP 先发送,Candidate 收集一个发一个),SDP 需包含
a=ice-options:trickle
。
优势:Trickle ICE 可缩短连接建立时间(无需等待所有 Candidate 收集完成),是 WebRTC 的默认方式。
SDP 中 Candidate 的格式示例:
a=candidate:240568271 1 udp 1686052607 174.139.8.82 64462 typ srflx raddr 10.1.1.19 rport 64462 generation 0 ufrag TWCy network-id 2
字段含义解析:
字段 | 含义 |
---|---|
240568271 | Foundation(Candidate 唯一标识) |
1 | Component ID(媒体类型:1=RTP,2=RTCP;WebRTC 默认用 RTCP-mux 合并为 1) |
udp | 传输协议(UDP/TCP) |
1686052607 | Priority(Candidate 优先级,数值越大优先级越高) |
174.139.8.82 | Candidate 地址(公网映射地址,对应 Srflx 类型) |
64462 | 端口号 |
typ srflx | Candidate 类型(host/srflx/prflx/relay) |
raddr 10.1.1.19 | Base 地址(Srflx 的本地内网地址) |
ufrag TWCy | ICE 用户名片段(用于 STUN 认证,与对端 ufrag 组合为 USERNAME) |
步骤 3:生成 Candidate Pair(候选地址对)
两端收到对方的 Candidate 后,按以下规则组合为 Candidate Pair(本端 Candidate + 远端 Candidate):
- 匹配条件:
Component ID 相同
(如均为 RTP 的 1)且传输协议相同
(如均为 UDP); - 地址修整:若 Candidate 为 Srflx,需用其 Base 地址(本地内网地址)替换,确保检查逻辑正确。
步骤 4:连通性检查(Connectivity Checks)
ICE 按 Candidate Pair 的优先级 排序(优先级高的先检查),通过 STUN Binding Request/Response 验证 “该地址对是否能互通”。
(1)Candidate Pair 优先级计算
优先级决定检查顺序,公式如下(核心逻辑:优先选择 “本地优先” 且 “对端优先” 的组合):
Pair Priority = 2^32 * MIN(G, D) + 2 * MAX(G, D) + (G > D ? 1 : 0)
G
:Controlling 角色的 Candidate 优先级;D
:Controlled 角色的 Candidate 优先级;MIN(G,D)
/MAX(G,D)
:取两者的最小值和最大值,确保 “高优先级 Candidate 优先匹配”。
(2)检查方式
- Ordinary Checks(普通检查):Controlling 端按优先级顺序,主动向 Controlled 端发送 STUN Binding Request;
- Triggered Checks(触发式检查):Controlled 端收到检查请求后,立即向 Controlling 端发起反向检查(提高效率,避免等待)。
(3)检查成功的条件
- 收到对端的 STUN Binding Success Response;
- 地址对称:请求的 “源地址 = 响应的目的地址” 且 “请求的目的地址 = 响应的源地址”(排除 NAT 地址映射不对称的情况)。
(4)重传机制
若未收到响应,ICE 按 指数退避策略 重传(初始间隔 50ms,每次翻倍,直至最大重传次数或超时)。
步骤 5:生成 Valid List(有效地址列表)
将 “连通性检查成功” 的 Candidate Pair 按优先级排序,组成 Valid List(可用路径列表)。
步骤 6:提名 Candidate Pair(Nomination)
由 Controlling 角色 从 Valid List 中选择 “最优路径” 并提名,分为两种提名方式:
- 普通提名(Regular Nomination):分两步检查
- 第一次检查:不带
USE-CANDIDATE
属性,仅验证连通性; - 第二次检查:对 Valid List 中的 Pair 发送带
USE-CANDIDATE
的请求,确认提名。
- 第一次检查:不带
- 进取型提名(Aggressive Nomination):一步完成
- 所有检查请求均带
USE-CANDIDATE
属性; - 第一个检查成功的 Pair 直接被提名(效率更高,WebRTC 默认采用)。
- 所有检查请求均带
步骤 7:建立加密连接(DTLS)
提名完成后,两端基于 “选中的 Candidate Pair” 建立 DTLS(数据报传输层安全)连接——WebRTC 要求所有数据(音视频、DataChannel)必须加密,DTLS 负责证书交换、密钥协商,确保传输安全。
步骤 8:连接保活(Keep-Alive)
NAT 网关会 “超时清理” 长期无数据的连接(默认超时时间 30s~5min),ICE 通过以下方式保活:
- 定期发送 STUN Binding Request/Indication(初始间隔 50ms,稳定后间隔 2.5s);
- 若未收到响应,触发重传或重新选择 Valid List 中的备用路径。
ICE 的状态流转
ICE 代理在工作过程中会经历以下状态,用于标识连接进展:
状态 | 含义 |
---|---|
Waiting | 未开始连通性检查,等待 Candidate 收集或交换完成 |
In-Progress | 连通性检查已启动,但尚未有成功的 Pair |
Succeeded | 已找到可用的 Candidate Pair,DTLS 连接建立完成,可开始数据传输 |
Failed | 所有 Candidate Pair 检查失败,且 TURN 中继不可用(连接彻底失败) |
Frozen | Candidate Pair 暂不检查(如等待其他 Pair 检查结果,后续可解冻) |
二、WebRTC ICE的意义
2.1 价值
- 解决 NAT 穿越难题:通过多 Candidate 类型和动态检查,覆盖 90% 以上的网络场景;
- 优先 P2P 通信:减少对中继服务器的依赖,降低延迟(P2P 延迟通常 <100ms,中继延迟可能> 200ms);
- 可靠性保障:Valid List 提供备用路径,某条路径中断时可快速切换到其他路径。
2.2 局限性
- 依赖信令服务器:ICE 需信令服务器交换 SDP 和 Candidate,无法独立工作;
- 对称型 NAT 需中继:对称型 NAT 下 P2P 无法打通,需依赖 TURN 服务器(增加带宽成本和延迟);
- 防火墙限制:部分严格防火墙会阻断 UDP 端口,需降级为 TCP/TLS(效率较低)。
2.3 总结
ICE 是 WebRTC 实现 “实时 P2P 通信” 的核心支柱,它通过 “收集候选地址→检查连通性→提名最优路径” 的闭环流程,绕开了 NAT 网关的限制,同时整合 STUN 和 TURN 实现 “P2P 优先、中继兜底” 的通信策略。理解 ICE 的工作原理,是排查 WebRTC 连接失败(如 “无法建立 P2P 连接”“延迟过高”)的关键 —— 例如,连接失败可能是 STUN/TURN 服务器不可用、NAT 类型为对称型且无中继资源,或 Candidate 交换不完整。