WebRTC 03:核心机制——NAT 穿透原理、P2P 打洞
深入理解 WebRTC 能够实现点对点(P2P)通信的底层魔法。为什么处于不同内网的设备能直接对话?什么是 NAT?STUN 和 TURN 到底在做什么?本篇将揭开 NAT 穿透与打洞技术的神秘面纱。
在上一篇中,我们了解了 WebRTC 的工作流程:Signaling 交换信息,ICE 建立连接。但其中最神奇的莫过于:两个处于不同 Wi-Fi、背后有层层路由器的设备,竟然能直接建立 P2P 连接。
这背后就是 NAT 穿透(NAT Traversal) 技术。
一、为什么需要穿透?—— IPv4 的困局与 NAT 的诞生
1.1 IP 地址不够用了
IPv4 只有约 43 亿个地址,随着设备激增,早已枯竭。
1.2 NAT(网络地址转换)
为了解决这个问题,NAT(Network Address Translation)应运而生。路由器拥有一个公网 IP,而路由器背后的设备(手机、电脑)使用私有 IP(如 192.168.x.x)。
- 出网:当你访问百度时,路由器把你的私有 IP
192.168.1.5:8000替换成路由器的公网 IP1.2.3.4:10001发出去。 - 回网:百度回复给
1.2.3.4:10001,路由器查表(NAT 映射表),发现这个端口对应192.168.1.5:8000,于是转发给你。
1.3 P2P 的难题
WebRTC 想要建立 P2P 连接,A 必须知道 B 的地址。但 B 只有一个私有 IP,A 无法直接访问。B 的公网 IP 是路由器的,而且路由器默认会丢弃不明来历的入站数据包(防火墙特性)。
只有当 B 内部先向 A 发送过数据,路由器留下了“洞”(映射记录),A 的数据才能通过这个“洞”钻进来。这就是“打洞”。
二、NAT 的四种类型(经典分类)
理解 NAT 类型是理解打洞成功率的关键。RFC 3489 定义了四种经典类型(虽然现代定义更复杂,但这个分类依然有助于理解):
1. 全锥型 NAT (Full Cone NAT) —— 最宽松
- 特性:一旦内部主机
IP:Port映射到公网IP:Port,任何外部主机发给公网IP:Port的数据都会被转发进来。 - 打洞难度:极低。只要 STUN 告诉你公网地址,直接给对方,对方就能连上。
2. 地址限制锥型 NAT (Address Restricted Cone NAT)
- 特性:映射建立后,只有刚才我发给过的那台外部主机 IP,才能给我发回数据。其他主机 IP 发来的会被丢弃。
- 打洞难度:低。
3. 端口限制锥型 NAT (Port Restricted Cone NAT) —— 最常见
- 特性:比上面更严。只有刚才我发给过的那个外部主机 IP + Port,才能给我发回数据。
- 现状:绝大多数家用路由器属于这一类。
- 打洞难度:中等。需要双方互相发包(互相打洞),才能在各自路由器上为对方打开通道。
4. 对称型 NAT (Symmetric NAT) —— P2P 杀手
- 特性:同一个内部
IP:Port,发给不同的外部目标地址时,路由器会分配不同的公网端口。- 比如:我给 STUN 服务器发,映射成
1.2.3.4:10001。 - 我给对端 Peer 发,映射可能就变成了
1.2.3.4:10002。
- 比如:我给 STUN 服务器发,映射成
- 现状:常见于大型企业网、3G/4G/5G 移动网络、部分公共 Wi-Fi。
- 打洞难度:极高(基本无法 P2P),通常只能走 TURN 中继。
三、P2P 打洞(Hole Punching)核心原理
打洞的核心思想是:欺骗 NAT,让它以为这还是那个“请求-响应”的会话。
场景模拟
- User A(在 NAT A 后)
- User B(在 NAT B 后)
- Server S(公网信令/STUN 服务器)
步骤 1:发现自我 (STUN)
A 和 B 分别向 S 发送请求。S 看到 A 的公网地址是 1.1.1.1:100,B 的是 2.2.2.2:200。S 把这些信息告诉 A 和 B。
步骤 2:交换地址 (Signaling)
A 通过信令服务器把自己的公网地址 1.1.1.1:100 告诉 B。B 也把 2.2.2.2:200 告诉 A。
步骤 3:尝试连接 (UDP Hole Punching)
此时,A 和 B 都知道了对方的公网 IP 和端口。
A -> B (1.1.1.1:100 -> 2.2.2.2:200)
- A 的路由器记录:允许
2.2.2.2:200进来。 - B 的路由器:收到来自 A 的包,但因为 B 还没给 A 发过数据(假设是端口限制型 NAT),B 的路由器认为这是攻击,丢弃。
- 结果:A 发送失败,但 A 的路由器上留下了给 B 的“洞”。
- A 的路由器记录:允许
B -> A (2.2.2.2:200 -> 1.1.1.1:100)
- B 的路由器记录:允许
1.1.1.1:100进来。 - A 的路由器:收到来自 B 的包。查表发现,A 之前给 B 发过(就在刚才),规则匹配!放行。
- 结果:A 收到 B 的包。通道建立!
- B 的路由器记录:允许
双向通信
- 既然 A 收到了 B,A 再给 B 发,B 的路由器查表发现 B 刚才也给 A 发过,放行。
- P2P 连接成功。
四、STUN 与 TURN 的职责
STUN (Session Traversal Utilities for NAT)
- 作用:
- 告诉我:我的公网 IP 和端口是什么?
- 探测 NAT 类型(辅助判断是否需要 TURN)。
- 保持 NAT 映射不过期(Keep-alive)。
- 本质:它只是一个反射镜。
- 流量:极小,只在建立连接初期使用。
TURN (Traversal Using Relays around NAT)
- 作用:当 P2P 打洞失败(如一方是对称型 NAT)时,充当中间人转发所有数据。
- 工作模式:
- A 请求 TURN 分配一个公网 relay 地址。
- A 把这个 relay 地址发给 B。
- B 发数据给 relay 地址,TURN 服务器转发给 A。
- 代价:延迟增加,服务器带宽消耗大(视频数据全走服务器)。
- 必要性:虽然我们想要 P2P,但为了保证 100% 的连通率,TURN 是必须的兜底方案。
五、ICE:连接的指挥官
ICE (Interactive Connectivity Establishment) 是 WebRTC 中负责整合上述所有机制的框架。它不发明新协议,而是把 STUN、TURN 组合起来使用。
ICE 工作流(简化版)
收集候选者 (Candidates):
- Host:本机内网 IP(如
192.168.1.5)。如果是局域网互通,这个最快。 - Srflx (Server Reflexive):通过 STUN 拿到的公网 IP。用于外网 P2P。
- Relay:通过 TURN 拿到的中继 IP。最后的救命稻草。
- Host:本机内网 IP(如
交换候选者:
- 通过信令发给对方。
连通性检测 (Connectivity Checks):
- 排列组合:A 的 Host 对 B 的 Host?A 的 Srflx 对 B 的 Srflx?…
- ICE 引擎会按优先级测试这些组合(通常 UDP 优先,直连优先)。
选路与提名:
- 只要有一条路通了,就可以开始传媒体。
- 如果发现更好的路(比如一开始走了 Relay,后来打洞成功了),会切换到更好的路(Srflx)。
六、总结与常见误区
| 概念 | 核心作用 | 形象比喻 |
|---|---|---|
| NAT | 隐藏内网,节省 IP | 小区的收发室,外人只知道小区地址,不知道具体门牌。 |
| STUN | 发现公网地址 | 照镜子,看自己在公网长什么样。 |
| TURN | 中继数据 | 传声筒,实在听不见时找中间人传话。 |
| ICE | 寻找最佳路径 | 导航软件,试探哪条路能通且最快。 |
| 打洞 | 建立直连映射 | 双方约好时间互相给对方收发室打电话,让看门大爷放行。 |