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 替换成路由器的公网 IP 1.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
  • 现状:常见于大型企业网、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 和端口。

  1. 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 的“洞”。
  2. B -> A (2.2.2.2:200 -> 1.1.1.1:100)

    • B 的路由器记录:允许 1.1.1.1:100 进来。
    • A 的路由器:收到来自 B 的包。查表发现,A 之前给 B 发过(就在刚才),规则匹配!放行
    • 结果:A 收到 B 的包。通道建立!
  3. 双向通信

    • 既然 A 收到了 B,A 再给 B 发,B 的路由器查表发现 B 刚才也给 A 发过,放行
    • P2P 连接成功。

四、STUN 与 TURN 的职责

STUN (Session Traversal Utilities for NAT)

  • 作用
    1. 告诉我:我的公网 IP 和端口是什么?
    2. 探测 NAT 类型(辅助判断是否需要 TURN)。
    3. 保持 NAT 映射不过期(Keep-alive)。
  • 本质:它只是一个反射镜。
  • 流量:极小,只在建立连接初期使用。

TURN (Traversal Using Relays around NAT)

  • 作用:当 P2P 打洞失败(如一方是对称型 NAT)时,充当中间人转发所有数据。
  • 工作模式
    1. A 请求 TURN 分配一个公网 relay 地址。
    2. A 把这个 relay 地址发给 B。
    3. B 发数据给 relay 地址,TURN 服务器转发给 A。
  • 代价:延迟增加,服务器带宽消耗大(视频数据全走服务器)。
  • 必要性:虽然我们想要 P2P,但为了保证 100% 的连通率,TURN 是必须的兜底方案。

五、ICE:连接的指挥官

ICE (Interactive Connectivity Establishment) 是 WebRTC 中负责整合上述所有机制的框架。它不发明新协议,而是把 STUN、TURN 组合起来使用。

ICE 工作流(简化版)

  1. 收集候选者 (Candidates)

    • Host:本机内网 IP(如 192.168.1.5)。如果是局域网互通,这个最快。
    • Srflx (Server Reflexive):通过 STUN 拿到的公网 IP。用于外网 P2P。
    • Relay:通过 TURN 拿到的中继 IP。最后的救命稻草。
  2. 交换候选者

    • 通过信令发给对方。
  3. 连通性检测 (Connectivity Checks)

    • 排列组合:A 的 Host 对 B 的 Host?A 的 Srflx 对 B 的 Srflx?…
    • ICE 引擎会按优先级测试这些组合(通常 UDP 优先,直连优先)。
  4. 选路与提名

    • 只要有一条路通了,就可以开始传媒体。
    • 如果发现更好的路(比如一开始走了 Relay,后来打洞成功了),会切换到更好的路(Srflx)。

六、总结与常见误区

概念核心作用形象比喻
NAT隐藏内网,节省 IP小区的收发室,外人只知道小区地址,不知道具体门牌。
STUN发现公网地址照镜子,看自己在公网长什么样。
TURN中继数据传声筒,实在听不见时找中间人传话。
ICE寻找最佳路径导航软件,试探哪条路能通且最快。
打洞建立直连映射双方约好时间互相给对方收发室打电话,让看门大爷放行。

This article was updated on January 25, 2026