一、为什么需要NAT穿透?

先问大家一个问题:两台电脑都在各自的公司内网里,它们能直接建立连接吗?

答案往往是不能。原因很简单——IPv4地址不够用了

全球IPv4地址总数约43亿个,听起来不少,但全球联网设备早就超过这个数了。于是NAT(网络地址转换)技术应运而生。它让一个公网IP可以给成百上千的内网设备共享使用。

我刚开始做WebRTC时,就踩过这个坑。本地测试一切正常,一部署到客户现场就炸了。后来才发现,客户的网络环境是典型的NAT场景,而我的代码根本没考虑穿透问题。

NAT的工作原理

NAT设备(通常是路由器)维护一张映射表。当内网设备访问外网时,NAT会分配一个公网端口,把内网IP:端口映射到公网IP:端口。

举个例子:

内网设备:192.168.1.100:12345
→ NAT映射后:203.0.113.5:65000
→ 访问目标:8.8.8.8:80

外网服务器看到的是203.0.113.5:65000,而不是真正的内网地址。这就是NAT的"障眼法"。

核心问题:两个内网设备想直接通信,但彼此都不知道对方的真实内网地址,只知道对方经过NAT映射后的公网地址。而这个映射关系,又不是固定不变的。

二、对称NAT的穿透难点

NAT有四种类型,其中对称NAT是最难对付的。我个人习惯把NAT类型分成两类:好对付的和不好对付的。对称NAT就属于后者。

NAT的四种类型

NAT类型 端口映射行为 穿透难度
完全锥形NAT 所有外部主机都能通过映射的端口访问 容易
受限锥形NAT 只有内网设备曾访问过的外部主机能访问 中等
端口受限锥形NAT 限制更严格,要求外部主机的IP和端口都匹配 较难
对称NAT 每次连接都分配不同的端口映射 极难

对称NAT为什么难穿透?

对称NAT的"狡猾"之处在于:它每次建立新连接,都会分配一个不同的公网端口

你想想看:

  • 设备A通过NAT-A访问服务器S,获得映射端口10001
  • 设备B通过NAT-B访问服务器S,获得映射端口20001
  • 服务器告诉A:B的公网地址是X.X.X.X:20001
  • 服务器告诉B:A的公网地址是Y.Y.Y.Y:10001
  • A尝试连接B的20001端口——但NAT-B只允许来自S的连接通过
  • A的这次新连接,NAT-A又分配了一个新端口10002

这就陷入死循环了。我在项目中遇到过这种情况,当时调试了整整两天才定位到问题。客户用的是某运营商的4G网络,恰好是对称NAT,导致视频通话始终建立不起来。

避坑指南:我曾经以为TURN服务器能解决所有问题,但对称NAT环境下,即使有TURN,如果配置不当,仍然可能失败。关键是要确保TURN服务器使用正确的传输协议(TCP或TLS)。

三、传统P2P通信的困境

传统的客户端-服务器模式,通信很简单:客户端连服务器,服务器转发数据。但P2P不一样,它想让两个客户端直接对话。

困境在哪?说白了就三点:

困境一:地址不可达

内网设备没有公网IP,外网设备无法主动连接它。即使知道它的公网映射地址,这个映射也可能随时失效。

困境二:映射不可预测

对称NAT的端口映射是随机的。你无法预测下一次连接会分配到哪个端口。这就好比两个人约见面,但每次见面地点都随机变化,而且双方都不知道对方的新地点。

困境三:防火墙拦截

很多企业网络有严格的防火墙策略。即使NAT穿透成功了,防火墙也可能把P2P流量当作攻击行为直接拦截。

我记得有个金融客户,他们的网络策略是只允许80和443端口通过。我们不得不把所有WebRTC流量伪装成HTTPS流量,才勉强跑通。

我的建议:做P2P通信开发,一定要提前了解目标网络的NAT类型。如果超过30%的用户是对称NAT,那就老老实实部署TURN服务器吧,别在穿透上死磕。

四、NAT穿透的核心逻辑

说了这么多问题,那NAT穿透到底是怎么实现的?我画了一张图,帮你理清思路。

NAT穿透核心逻辑 内网A 公网(信令服务器) 内网B 设备A 192.168.1.100:12345 NAT-A 映射: 203.0.113.5:65000 信令服务器 交换SDP/Candidate 设备B 192.168.1.200:54321 NAT-B 映射: 198.51.100.7:32000 ① 注册/请求 ② 转发地址 ③ 回复地址 ④ 返回B的地址 ⑤ P2P直连尝试 穿透成功?取决于NAT类型和端口预测准确性

流程其实不复杂:

  1. 设备A通过信令服务器告诉设备B自己的地址
  2. 设备B也通过信令服务器告诉设备A自己的地址
  3. 双方尝试直接连接对方
  4. 如果NAT类型友好,连接成功
  5. 如果遇到对称NAT,连接失败,需要TURN中继

嗯,这里要注意:信令服务器只负责交换地址信息,不传输媒体数据。这是WebRTC设计上的一个精妙之处——控制面和数据面分离。

总结一下:NAT穿透的本质,就是想办法让两个内网设备"猜"到对方的真实映射地址,并在映射失效前完成连接。对称NAT之所以难,是因为它让这个"猜测"变得几乎不可能。

我在实际项目中,遇到过最极端的情况:两个设备都在对称NAT后面,而且NAT的端口分配策略是每30秒变化一次。这种情况下,别说P2P穿透了,连维持连接都困难。最后只能走TURN中继,虽然延迟高了一些,但至少能通。

所以,做WebRTC开发,一定要有"穿透失败"的心理准备。不要把所有希望都寄托在P2P上,该上TURN就上TURN。毕竟,用户只关心视频能不能打通,才不管你是P2P还是中继。


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