NAT穿透:NAT类型与WebRTC的穿透策略

说到NAT穿透,我得先跟你聊聊我早年踩过的一个坑。那时候我刚接触WebRTC,满心以为只要两端都连上网络,视频通话就能自动通。结果呢?本地测试好好的,一放到公网环境,画面就是出不来。折腾了两天,最后发现是NAT在搞鬼。

说白了,NAT就是你家路由器干的事——把内网IP映射成公网IP。但不同的路由器,映射方式不一样。这就导致了WebRTC能不能打通,完全取决于NAT的类型。

NAT的四种类型

我习惯把NAT分成四类,从最宽松到最严格。你想想看,这就像小区门禁系统,有的随便进,有的要刷卡,有的还要对暗号。

1. 完全锥形NAT(Full Cone NAT)

这是最友好的类型。内网主机A(192.168.1.100:5000)一旦向外发送过数据包,NAT就会在公网侧开一个端口(比如 203.0.113.5:8000)。任何外网主机只要往这个公网IP+端口发数据,都能穿透进来。

特点: 映射关系一旦建立,谁来访问都放行。

我在项目中遇到过:用STUN打洞时,完全锥形NAT几乎一次就能成功,成功率接近100%。

2. 限制锥形NAT(Restricted Cone NAT)

比完全锥形严格一点。它要求:外网主机必须先收到内网主机发过去的包,才能回传数据。换句话说,只有内网主机主动联系过的外网IP,才能穿透进来。

嗯,这里要注意:它只检查IP,不检查端口。所以只要IP对了,端口随便换都能通。

3. 端口限制锥形NAT(Port Restricted Cone NAT)

这个更严格了。不仅要求IP匹配,还要求端口也必须匹配。内网主机向 8.8.8.8:53 发了包,那只有 8.8.8.8:53 这个组合能回传数据。换个端口都不行。

我曾经调试过一个客户的环境,就是这种NAT。当时花了大半天才定位到问题——不是代码写错了,是NAT太严了。

4. 对称NAT(Symmetric NAT)

这是最难搞的类型。每次内网主机向不同的目标IP+端口发数据,NAT都会分配不同的公网端口。举个例子:

  • A → B(1.2.3.4:80),NAT映射为 203.0.113.5:8000
  • A → C(5.6.7.8:80),NAT映射为 203.0.113.5:8001

你看,同一个内网主机,同一个端口,只是目标不同,公网端口就变了。这就导致STUN服务器告诉你的映射地址,只对STUN服务器自己有效。其他主机想用这个地址打进来?门都没有。

避坑指南: 对称NAT是WebRTC穿透的头号敌人。我曾经在部署一个视频会议系统时,发现30%的用户都卡在对称NAT上。后来不得不引入TURN中继服务器才解决。

四种NAT对比

NAT类型 IP检查 端口检查 映射一致性 穿透难度
完全锥形 不检查 不检查 固定 ★☆☆☆☆
限制锥形 检查 不检查 固定 ★★☆☆☆
端口限制锥形 检查 检查 固定 ★★★☆☆
对称NAT 检查 检查 不固定 ★★★★★

WebRTC如何穿透NAT

WebRTC的穿透策略,说白了就是一套组合拳。我把它总结为三步走:

第一步:收集候选地址(ICE Candidate)

WebRTC会收集三种类型的候选地址:

  • Host候选: 本机内网IP,比如 192.168.1.100:5000
  • Srflx候选: 通过STUN服务器获取的公网映射地址
  • Relay候选: 通过TURN中继服务器分配的转发地址

我习惯把这一步叫做「摸清自己的底牌」。只有知道自己有哪些地址可用,才能决定下一步怎么打洞。

第二步:连通性检查(Connectivity Check)

双方交换候选地址后,开始互相发送STUN绑定请求。这个过程我简单说一下:

// 伪代码示意
Peer A 的候选列表: [192.168.1.100:5000, 203.0.113.5:8000, turn:relay.com:3478]
Peer B 的候选列表: [10.0.0.5:6000, 198.51.100.7:9000, turn:relay.com:3478]

// 检查顺序(优先级从高到低):
// 1. A的Host → B的Host(局域网直连)
// 2. A的Host → B的Srflx
// 3. A的Srflx → B的Srflx(NAT穿透)
// 4. 最后才用Relay(TURN中继)

为什么会按这个顺序?因为Host最快,Relay最慢还费带宽。我一般建议优先尝试P2P直连,实在不行再走中继。

第三步:NAT穿透的具体策略

针对不同的NAT类型,WebRTC会采用不同的策略:

对于完全锥形、限制锥形、端口限制锥形:

WebRTC通过STUN获取公网映射地址后,直接进行连通性检查。只要双方都发过包,NAT就会放行。成功率很高,我实测在80%以上。

对于对称NAT:

这是最头疼的情况。WebRTC的标准做法是:如果P2P打洞失败,就回退到TURN中继。但有个技巧——我曾经试过用「端口预测」法,就是猜测对称NAT下一个分配的端口号。虽然成功率不高(大概30%),但聊胜于无。

核心逻辑流程图

下面这张图,是我自己总结的WebRTC NAT穿透决策流程。你看一眼就能明白整个逻辑:

WebRTC NAT穿透决策流程 开始收集候选地址 收集 Host / Srflx / Relay 候选 通过 STUN 获取公网映射地址 连通性检查 是否成功? 成功 P2P 直连建立 使用 Srflx 候选通信 失败 判断 NAT 类型 是否对称NAT? 回退到 TURN 中继 通过 Relay 候选转发 重试 P2P 打洞 使用端口预测等技巧 通信建立完成

实际项目中的经验

我做了这么多年的WebRTC开发,总结了几条实战经验:

  1. STUN服务器一定要部署多个。 我曾经只部署了一个STUN服务器,结果那台机器挂了,所有用户都打不通。后来我至少部署3个,分布在不同的机房。
  2. TURN服务器是最后的保险。 虽然它费带宽(转发流量),但能保证100%连通。我一般建议预留TURN带宽,至少能承载20%的并发用户。
  3. ICE重启是个好技巧。 如果第一次打洞失败,可以触发ICE重启,重新收集候选地址。有时候换个端口就能通。

小提示: 在调试NAT穿透问题时,我习惯用 chrome://webrtc-internals 这个工具。它能实时显示ICE候选、连通性检查的状态,比看日志方便多了。

好了,关于NAT穿透的核心内容就这些。你想想看,其实WebRTC的穿透策略并不复杂——就是先试直连,不行就中继。但真正难的是理解NAT的行为,以及在不同场景下选择合适的策略。希望这些经验能帮你少走弯路。


公众号:蓝海资料掘金营,微信deep3321