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收集完候选者后,会按优先级排序。规则很简单:
- host候选者优先级最高(延迟最低)
- srflx候选者次之
- 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穿透的逻辑串起来:
核心要点回顾:
- NAT类型决定了穿透难度,对称NAT最难搞
- ICE通过三步(收集→排序→检查)找到最优连接路径
- STUN免费但有限,TURN费钱但可靠
- 部署策略:STUN为主,TURN兜底
我的小建议:刚开始做WebRTC开发时,别急着部署TURN服务器。先用Google的公共STUN服务器(stun:stun.l.google.com:19302)测试。等你的应用用户量上来了,再考虑自建TURN。这样能省不少带宽费。
注意:如果你在生产环境发现ICE连接总是失败,先别怀疑代码。大概率是网络环境的问题。我曾经花了两天排查代码,最后发现是客户的防火墙把UDP全封了。解决办法?要么开UDP端口,要么强制走TCP(但延迟会高一些)。
公众号:蓝海资料掘金营,微信deep3321