10、ICE与NAT穿透:NAT类型详解

好,咱们今天聊一个让很多WebRTC初学者头疼的话题——NAT穿透。说实话,我当年第一次接触这个知识点时,也是一头雾水。什么锥形、对称、STUN、TURN……这些名词堆在一起,简直像在看天书。

但你别怕。这东西说白了,就是解决一个问题:两个在不同网络里的设备,怎么找到对方并建立连接

咱们一步步拆开来看。

10.1 为什么需要NAT穿透?

先问个问题:你的电脑在局域网里,IP是192.168.1.10。你想跟另一个局域网里的朋友视频通话。你们俩的IP都是私有的,互联网上根本找不到你们。怎么办?

这就是NAT(网络地址转换)干的事。路由器把你的私有IP映射成一个公网IP+端口。但问题来了——不同的路由器,映射方式不一样。有的好说话,有的特别严格。

我在项目中遇到过最头疼的情况:双方都在对称NAT后面,结果STUN完全失效,最后只能上TURN中继。那延迟,啧啧……

10.2 NAT类型详解

NAT类型分四种。我建议你记住这张表,面试经常考:

NAT类型 映射规则 穿透难度 典型场景
完全锥形NAT 所有外部主机都能访问映射后的端口 容易 家用路由器(宽松模式)
限制锥形NAT 只有内网主机曾发过数据的外部IP才能访问 中等 企业防火墙
端口限制锥形NAT 限制IP+端口,必须精确匹配 较难 运营商级NAT
对称NAT 每次连接使用不同的端口映射 极难 4G/5G移动网络

我简单解释一下区别:

  • 完全锥形:你开了个门,谁都能进来。最宽松。
  • 限制锥形:你只允许认识的人进来。但认识的人换端口也行。
  • 端口限制锥形:你只允许认识的人从指定的门口进来。严格多了。
  • 对称NAT:每次你出门,都换一个不同的门。别人根本找不到你。

嗯,这里要注意:对称NAT是WebRTC的噩梦。我曾经调试过一个移动端项目,用户在4G网络下,ICE协商了整整30秒才连上——就是因为对称NAT导致STUN失效,最后靠TURN才搞定。

10.3 ICE工作原理

ICE(交互式连接建立)是WebRTC的"智能连接器"。它的工作分三步:

10.3.1 收集候选者(Candidate)

ICE会收集所有可能的连接方式。每种方式就是一个Candidate。分三种:

  • host候选者:本机IP,比如192.168.1.10:9000
  • srflx候选者:通过STUN获取的公网IP映射,比如203.0.113.5:45000
  • relay候选者:通过TURN中继服务器分配的地址

代码里长这样:

// 创建RTCPeerConnection时,ICE就开始收集候选者了
const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { 
      urls: 'turn:turn.example.com:3478',
      username: 'user',
      credential: 'pass'
    }
  ]
});

// 监听候选者事件
pc.onicecandidate = (event) => {
  if (event.candidate) {
    console.log('收集到候选者:', event.candidate.candidate);
  }
};

我个人习惯在onicecandidate里加个计数器,看看收集到了几个候选者。如果只有host类型,那基本可以断定——STUN/TURN配置有问题。

10.3.2 排序候选者

ICE收集完候选者后,会按优先级排序。规则很简单:

  1. host候选者优先级最高(延迟最低)
  2. srflx候选者次之
  3. relay候选者优先级最低(延迟最高)

ICE会优先尝试host直连。不行再试srflx。最后才用relay。说白了,ICE的哲学是:能直连绝不绕路

10.3.3 连通性检查

这是最核心的一步。ICE会拿自己的候选者列表,跟对方的候选者列表做配对。每对组合都发STUN探测包,看能不能通。

举个例子:

  • 你的host候选者(192.168.1.10:9000)
  • 对方的srflx候选者(203.0.113.8:32000)

ICE会从你的9000端口,向对方的32000端口发一个STUN请求。如果对方回复了,说明这对候选者可用。

我曾经遇到过一个问题:连通性检查一直失败,但两边网络明明没问题。后来发现是防火墙把STUN探测包当攻击流量拦截了。解决办法?在防火墙上放行UDP 3478端口。

10.4 STUN与TURN的部署策略

STUN和TURN,很多人搞混。我一句话说清楚:

  • STUN:帮你找到自己的公网地址。轻量级,免费。
  • TURN:帮你中继数据。重量级,费带宽。

部署策略其实就一条:能用STUN就别用TURN

但现实很骨感。根据我的经验,大约15%-20%的场景下STUN会失效(主要是对称NAT和防火墙限制)。这时候TURN就是救命稻草。

我建议的部署方案:

场景 推荐策略 说明
局域网/内网 仅STUN host候选者就够了
公网对公网 STUN + 备用TURN 大部分情况STUN能搞定
移动网络 必须部署TURN 对称NAT太常见了
高可靠性要求 多TURN服务器 做负载均衡和容灾

部署TURN时,我踩过一个坑:带宽成本。TURN服务器把所有媒体流都过一遍,一个视频通话可能吃掉几Mbps的带宽。如果用户量上来,带宽费用会吓死人。所以我的建议是——只在STUN失败时才启用TURN

代码里怎么控制?ICE的候选者优先级可以调:

// 设置TURN候选者优先级低于STUN
const pc = new RTCPeerConnection({
  iceTransportPolicy: 'relay' // 强制只用TURN(不推荐)
  // 默认是'all',即所有候选者都尝试
});

嗯,这里要注意:iceTransportPolicy: 'relay'是最后的手段。我只有在调试时才用这个选项。生产环境千万别这么干——所有流量都走TURN,你的服务器会哭的。

10.5 知识体系总览

说了这么多,咱们用一张图把整个ICE和NAT穿透的逻辑串起来:

ICE与NAT穿透知识体系 NAT类型 完全锥形 限制锥形 端口限制锥形 对称NAT ICE工作原理 ① 收集候选者 ② 排序候选者 ③ 连通性检查 候选者类型 host(本机IP) srflx(STUN映射) relay(TURN中继) STUN vs TURN 部署策略 STUN:轻量级,获取公网映射地址 TURN:重量级,中继媒体流(仅STUN失败时启用) 核心原则:能直连绝不绕路,STUN优先,TURN兜底 ⚠ 避坑:对称NAT下STUN基本失效,必须部署TURN

核心要点回顾:

  • NAT类型决定了穿透难度,对称NAT最难搞
  • ICE通过三步(收集→排序→检查)找到最优连接路径
  • STUN免费但有限,TURN费钱但可靠
  • 部署策略:STUN为主,TURN兜底

我的小建议:刚开始做WebRTC开发时,别急着部署TURN服务器。先用Google的公共STUN服务器(stun:stun.l.google.com:19302)测试。等你的应用用户量上来了,再考虑自建TURN。这样能省不少带宽费。

注意:如果你在生产环境发现ICE连接总是失败,先别怀疑代码。大概率是网络环境的问题。我曾经花了两天排查代码,最后发现是客户的防火墙把UDP全封了。解决办法?要么开UDP端口,要么强制走TCP(但延迟会高一些)。


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