4、ICE协议与NAT穿透:NAT类型、ICE工作原理、STUN与TURN服务器、Candidate优先级与类型

WebRTC 要解决的核心问题,说白了就是:两个浏览器怎么在复杂的网络环境下找到对方,并建立一条能用的数据通道。

现实世界里的网络,大部分设备都躲在 NAT(网络地址转换)后面。你想想看,你的电脑拿到的往往是 192.168.x.x 这样的内网 IP,而互联网上的其他设备根本不知道这个地址在哪。ICE 协议就是专门解决这个问题的。

我个人习惯把 ICE 比作一个“网络侦探”——它负责收集所有可能的连接方式,然后逐一尝试,直到找到一条能通的路。

4.1 NAT 类型:你的网络到底有多“封闭”?

在讲 ICE 之前,得先搞清楚 NAT 的分类。不同的 NAT 行为,决定了穿透的难度。

我在项目中遇到过最头疼的情况,就是对称型 NAT。这种 NAT 几乎无法用单纯的 STUN 穿透。

NAT 类型 行为特征 穿透难度
完全锥型 映射后的公网 IP:Port 对任何外部主机都开放 容易
限制锥型 只允许曾经发过包的目标 IP 回包 中等
端口限制锥型 限制更严格,必须 IP 和 Port 都匹配 较难
对称型 每次发往不同目标,都使用不同的公网端口 极难
我曾经踩过的坑:对称型 NAT 下,STUN 服务器返回的映射地址,只对 STUN 服务器本身有效。当你用这个地址去连接对端时,NAT 会重新分配一个新端口。结果就是——两边都以为拿到了正确的地址,但谁也连不上谁。

4.2 ICE 工作原理:从“猜”到“通”

ICE(Interactive Connectivity Establishment)的核心逻辑其实不复杂:收集候选者 → 排序 → 连通性检查 → 选定最终路径。

嗯,这里要注意,ICE 并不是一股脑把所有候选者都试一遍。它有一个优先级机制,会优先尝试理论上成功率最高的组合。

下面这张图展示了 ICE 的完整工作流程:

ICE 工作流程 ICE 协议工作流程 应用启动 / 创建 PeerConnection 收集候选者(host / srflx / relay) 通过信令交换候选者(SDP) 按优先级排序候选者对 逐对进行连通性检查(STUN 绑定请求) 选定可用路径,开始媒体传输 说明:连通性检查阶段会尝试所有候选者对,直到找到一条可用的路径

ICE 的检查策略是“先快后慢”。它会先尝试延迟最低的候选者对,比如两台机器在同一个局域网内。如果不行,再尝试通过 STUN 打洞。最后实在不行,才走 TURN 中继。

4.3 STUN 与 TURN 服务器:打洞与中继

STUN 和 TURN 是 ICE 的两大“武器”。它们的作用完全不同。

  • STUN(Session Traversal Utilities for NAT):帮设备发现自己的公网 IP 和端口。说白了就是“照镜子”。
  • TURN(Traversal Using Relays around NAT):当打洞失败时,作为中继转发数据。代价是带宽和延迟。

我记得有一次做跨国 WebRTC 通话测试,两边都是对称型 NAT,STUN 完全失效。最后只能走 TURN 中继,延迟从 20ms 飙升到 200ms。虽然能通,但体验确实差了不少。

关键区别:

  • STUN 只负责“发现地址”,不转发媒体数据。
  • TURN 负责“转发数据”,但会消耗服务器带宽。
  • ICE 会优先尝试 STUN 打洞,TURN 是最后的手段。

4.4 Candidate 优先级与类型

ICE 候选者(Candidate)分为三种类型,每种都有自己的优先级计算公式。

候选者类型 代码标识 来源 优先级范围
Host host 本地网卡 IP(内网地址) 最高(2130706431 ~ 1694498815)
Server Reflexive srflx STUN 服务器返回的公网映射地址 中等(1694498815 ~ 1007054847)
Relay relay TURN 服务器分配的中继地址 最低(1007054847 ~ 0)

优先级计算公式是这样的:

priority = (2^24) * type_preference + (2^8) * local_preference + (2^0) * component_id

其中 type_preference 对于 host 是 126,srflx 是 100,relay 是 0。这个公式确保了 host 候选者永远优先于 srflx,srflx 永远优先于 relay。

我个人的调试习惯:在 Chrome 的 chrome://webrtc-internals 页面里,可以直接看到每个候选者的优先级数值。如果发现 relay 候选者被选中了,基本可以断定是 NAT 穿透失败。

最后提一下 ICE 提名(Nomination)。当连通性检查通过后,ICE 会“提名”一个候选者对作为最终路径。这个过程分为两种模式:

  • 常规提名:等所有检查完成后再选最优的。
  • 激进提名:发现第一个可用的就直接用,不等了。

WebRTC 默认使用激进提名。为什么?因为快。用户体验上,早一秒建立连接比晚一秒但路径稍微好一点更重要。我在项目中验证过,激进提名在大多数场景下都能在 200ms 内完成连接建立。