← 返回题目列表

NAT 打洞(P2P 穿透)原理是什么?STUN、TURN、ICE 分别是什么?

高频 困难 第 17 / 27 题 更新于 2026/07/28
NAT打洞STUNTURNICEWebRTC

简化版

NAT 打洞是让两个都在 NAT 后面的内网节点直接建立点对点(P2P)连接的技术。核心难点:双方都没有公网 IP,都无法主动连对方。打洞的思路是借助一台公网上的协调服务器,让双方先各自弄清「自己经过 NAT 后对外呈现的公网 IP:端口」,交换给对方,然后双方同时向对方的公网映射地址发包,「撞开」各自 NAT 上的映射,从而建立直连。三个关键角色:STUN——帮节点探测「我经过 NAT 后的公网地址是什么」;TURN——打洞失败时的中继兜底服务器,双方都把流量发给它转发(牺牲直连,保证能通);ICE——一个框架/流程,收集各种候选地址(本地、STUN 得到的公网、TURN 中继),逐一尝试连通,优先直连、失败回退中继,选出最优通路。WebRTC 用的就是 ICE。

详细版

一、为什么打洞难:双方都在 NAT 后

内网穿透如果只有一方在 NAT 后,可以靠公网中转服务器解决。但 P2P 场景两个节点都在 NAT 后(比如两个家庭网络里的用户视频通话),双方都无公网 IP、都无法被对方主动连接。要让它们直连(不浪费中转带宽、低延迟),就得打洞。

二、打洞的关键:NAT 的映射是「出方向创建」的

NAT 的规则:内网设备主动往外发包时,NAT 会创建一条「私有地址:端口 ↔ 公网地址:端口」的映射,此后发到这个公网地址:端口的回包能被送回该内网设备。打洞正是利用这点——让双方都主动往对方发包,各自在自己 NAT 上「打出」一个洞(映射),使对方的包能进来

三、三个角色

角色作用
STUN节点向 STUN 服务器发包,服务器告诉它「你在我这看到的公网 IP:端口是 X」,节点由此知道自己的公网映射地址
TURN打洞失败时的中继服务器:双方都把数据发给 TURN,由它转发(不再是直连,但保证连通)
ICE总流程框架:收集所有可能的地址候选(本地/STUN 公网/TURN 中继),配对逐一连通性测试,优选直连、兜底中继

四、打洞的大致流程

① A、B 各自向 STUN 服务器发包,得到自己的公网映射地址
② 通过信令服务器(业务自己的服务器)交换彼此的公网映射地址
③ A、B 【同时】向对方的公网映射地址发包
   —— A 发给 B 的包,在 A 的 NAT 上打出映射;B 发给 A 的包同理
   —— 于是双方 NAT 上都有了「允许对方进来」的映射,直连打通
④ 若打洞失败(如对称 NAT),回退用 TURN 中继

五、NAT 类型决定成败

  • 锥形 NAT(Full Cone / 端口受限等):同一内网端口出去,对外映射相对固定/可预测,容易打通
  • 对称型 NAT(Symmetric)去往不同目标用不同的映射端口,STUN 探到的端口和实际发给对方时用的端口不一样,对方猜不到打洞常失败,只能走 TURN 中继。

完整版教学

一、场景设定:两个内网节点想直连

先想清楚打洞解决的问题。假设 A 在家(NAT_A 后),B 也在家(NAT_B 后),两人要视频通话,希望音视频流直接在 A、B 之间走(P2P),不经过服务器中转(省服务器带宽、延迟低、隐私好)。

难点:A、B 都没有公网 IP,都在各自 NAT 后面,谁也无法主动连上谁。这就是 NAT 打洞要攻克的。

二、原理基石:NAT 映射由「主动外发」创建

理解打洞,必须先牢记 NAT 的行为:

  • 内网 A_priv 主动向外发包,NAT_A 会创建映射:A_priv → A_pub(一个对外的公网 IP:端口)。
  • 之后,发往 A_pub 的包,NAT_A 认为是「A 主动通信的回包」,会放行并转回 A
  • 没有对应映射时,外来包一律丢弃

打洞的核心花招就是:让 A 和 B 都主动往对方发包,从而各自在自己的 NAT 上「预先打出」一个针对对方的映射(洞)。一旦两边的洞都打好,A 发给 B 的包能穿过 NAT_B(因为 B 已经往 A 发过包、NAT_B 上有 A 的映射),反之亦然——直连就通了。

三、STUN:先搞清「我的公网长相」

要往对方的公网地址发包,首先双方得知道自己经过 NAT 后对外是什么公网 IP:端口——内网设备自己是不知道的(它只知道自己的私有地址)。

STUN(Session Traversal Utilities for NAT) 就干这个:

A → 向公网 STUN 服务器发一个请求
STUN 服务器 → 看到这个包的源地址(即 NAT_A 转换后的公网地址),
              把「我看到你的地址是 A_pub:port」告诉 A
A → 于是知道了自己的公网映射地址

B 同理。这样双方都拿到了自己的公网映射地址,接下来通过信令服务器(业务自己的服务器,负责撮合双方、交换信息)把各自的公网地址告诉对方。

四、打洞:双方同时发包,互相撞开映射

拿到对方的公网地址后,A、B 几乎同时向对方的公网映射地址发包:

A 向 B_pub 发包:
   - 出 NAT_A 时,NAT_A 记下映射「A 正在和 B_pub 通信」
   - 到 NAT_B 时,若 B 还没往 A 发过包,NAT_B 可能先丢弃(洞还没打好)
B 向 A_pub 发包(同时):
   - 出 NAT_B 时,NAT_B 记下映射「B 正在和 A_pub 通信」
   - 到 NAT_A 时,因为 A 刚才发过、NAT_A 上已有 B 的映射,放行 → 到达 A
之后 A 再发的包,NAT_B 也已有 A 的映射 → 放行
   ⇒ 双向的洞都打好,P2P 直连建立

关键是**「同时发」**:让双方几乎在同一时间往对方发包,使各自 NAT 上的映射都建立起来,后续互发的包就都能穿过对方的 NAT 了。这就是「打洞(Hole Punching)」这个名字的由来——在 NAT 墙上互相凿出一个洞

五、NAT 类型:为什么有时打不通

打洞成败高度依赖 NAT 的类型,重点区分:

  • 锥形 NAT(Cone):内网某个 私有IP:端口 出去,NAT 分配一个固定的公网端口,无论发给哪个外部目标都用这个映射(或映射可预测)。这样 STUN 探到的公网端口,就是 A 实际发给 B 时用的端口——B 能命中,容易打通
  • 对称型 NAT(Symmetric):内网同一个 私有IP:端口发给不同的外部目标会分配不同的公网端口。于是 A 发给 STUN 服务器时用端口 P1(STUN 告诉 A 是 P1),但 A 发给 B 时 NAT 又分配了新端口 P2——B 收到的来自 A 的端口是 P2,而 B 按 STUN 给的 P1 去发根本打不中。双方端口互相预测不了,打洞通常失败

现实中还有更细的分类(Full Cone / Restricted / Port-Restricted / Symmetric),以及「一方锥形一方对称」等组合,成败各异。只要有一方是对称 NAT,打洞成功率就大幅下降

六、TURN:打洞失败的中继兜底

打洞不能保证 100% 成功(对称 NAT、复杂网络)。为了「无论如何都能通」,需要兜底方案——TURN(Traversal Using Relays around NAT)

  • TURN 是一台公网中继服务器
  • 打洞失败时,A 和 B 都把数据发给 TURN 服务器,由 TURN 转发给对方。
  • 这不再是直连(回到了「中转」模式),牺牲了 P2P 的低延迟和省带宽,但保证连通性

所以 TURN 是「实在打不通时的保底」,代价是流量都过中继、消耗服务器带宽(TURN 服务器成本较高)。

七、ICE:把这一切编排起来

前面这些(本地地址、STUN 探到的公网地址、TURN 中继地址)到底用哪个?由谁决定?——ICE(Interactive Connectivity Establishment) 是把它们统一编排的框架/流程

① 收集候选地址(Candidates):
   - host 候选:本地网卡地址(同一内网直接可用)
   - srflx 候选:STUN 得到的公网映射地址(用于打洞)
   - relay 候选:TURN 中继地址(兜底)
② 双方通过信令交换各自的候选列表
③ 对候选两两配对,做【连通性检查】(互发探测包看能不能通)
④ 按优先级选出能通的最优路径:
   优先【直连 host】> 【打洞 srflx】> 最后才用【TURN relay 中继】
⑤ 用选中的通路传输数据(媒体流等)

ICE 的精髓:不预设哪种方式,而是把所有可能的路都列出来,实测哪条通、哪条最优,优先直连、兜底中继WebRTC(浏览器实时音视频)用的正是 ICE + STUN + TURN 这套组合,这也是面试高频结合点。

八、三者关系理清

  • STUN:告诉你「你的公网地址」,是打洞的前提
  • TURN:打洞失败时的中继兜底
  • ICE总指挥,收集 STUN/TURN/本地等所有候选,实测选路,优先直连、兜底中继。

ICE ⊃ 使用 STUN 和 TURN。STUN/TURN 是工具,ICE 是用这些工具的完整策略。

九、常见误区与追问

考点正确口径
STUN探测公网映射地址和 NAT 类型
TURN直连失败时通过中继转发流量
ICE收集候选地址并选择最优连通路径
candidates:
host 192.168.1.10
srflx 203.0.113.8:40000 from STUN
relay 198.51.100.9:50000 from TURN
ICE checks pairs -> select working path

STUN 负责发现,TURN 负责兜底转发,ICE 负责把候选路径试出来并选最合适的。

  • 误区:STUN 能穿透所有 NAT。 STUN 只能帮助发现映射,对称 NAT 或严格防火墙下常需 TURN。
  • 误区:TURN 只是备用信令。 TURN 是媒体/数据中继,流量真的会经过 TURN 服务器,成本和延迟更高。
  • 误区:ICE 是一个单独中继服务。 ICE 是连接建立框架,组合 host、STUN、TURN 候选并做连通性检查。
  • 追问:为什么需要 NAT 穿透? 双方都在 NAT 后面时,公网无法直接主动连接内网地址。
  • 追问:WebRTC 为什么离不开 ICE? WebRTC 要在复杂 NAT 环境下建立 P2P 媒体路径,需要自动候选和探测。
  • 追问:TURN 成本为什么高? 所有媒体流量经服务器转发,消耗带宽并增加路径延迟。

十、加强记忆

NAT 打洞 = 让两个都在 NAT 后的节点直连,利用「NAT 映射由主动外发创建」这一点,让双方同时向对方的公网映射地址发包,互相在 NAT 墙上凿洞,之后互发的包就能穿过。三角色:STUN(探测「我的公网 IP:端口」,打洞前提)、TURN(打洞失败时的中继兜底,牺牲直连保连通)、ICE总框架:收集 host/STUN 公网/TURN 中继候选,实测选路,优先直连 > 打洞 > 中继)。成败看 NAT 类型锥形易通,对称型常失败(发不同目标用不同端口,对方猜不到)→ 回退 TURN。WebRTC = ICE + STUN + TURN。核心一句:STUN 找到自己的公网门牌,双方同时对撞打洞,打不通就让 TURN 中转,全程由 ICE 编排选最优路。