8、网络穿透技术:NAT类型详解、STUN协议工作原理、TURN中继服务器部署

聊到WebRTC,有个绕不开的话题——网络穿透。说白了,就是两个设备在不同的局域网里,怎么找到对方并建立连接。我刚开始接触WebRTC时,觉得这步很简单,不就是IP加端口嘛。结果第一次联调就翻车了,两边设备死活连不上。后来才明白,NAT这堵墙,远比我想象的复杂。

8.1 NAT类型详解

NAT(网络地址转换)是路由器用来让多个设备共享一个公网IP的技术。但不同的NAT类型,对穿透的影响天差地别。我个人习惯把NAT分为四种类型,从宽松到严格排列:

NAT类型 特点 穿透难度
完全锥形NAT 只要内网设备发出过请求,外部任何主机都能通过这个映射地址访问它 容易
受限锥形NAT 外部主机必须收到过内网设备的请求,才能回包 中等
端口受限锥形NAT 不仅限制IP,还限制端口。外部主机的端口必须匹配 较难
对称NAT 每次请求不同的外部目标,都会分配不同的端口映射 极难

关键点:对称NAT是WebRTC穿透的头号杀手。我曾经在一个项目中,80%的连接失败都源于对称NAT。遇到它,STUN基本没戏,必须上TURN。

为什么会这样?你想想看,对称NAT每次发请求都换端口,STUN服务器告诉你的公网地址,下一秒可能就失效了。另一个对端拿着这个地址打洞,就像往移动的靶子上射箭——基本靠蒙。

8.2 STUN协议工作原理

STUN(Session Traversal Utilities for NAT)的作用很简单:帮我找到自己在公网上的IP和端口。但它的工作流程,其实藏着不少细节。

基本流程分三步:

  1. 客户端向STUN服务器发送Binding Request
  2. STUN服务器收到请求,看到源IP和端口(这就是NAT映射后的地址)
  3. 服务器把看到的地址封装在Binding Response里返回给客户端

嗯,这里要注意。STUN不只是拿个地址就完事了。它还能帮你判断NAT类型。我记得有一次排查问题,发现客户端拿到的地址和实际不一致,后来发现是运营商做了二次NAT。这种情况,STUN拿到的地址其实是运营商级的,不是真正的公网IP。

实战技巧:我建议在STUN请求中携带多个属性,比如SOFTWARE属性标明客户端版本,方便后期排查。另外,STUN的Transaction ID一定要随机生成,避免冲突。

STUN的局限性也很明显——它只能帮你拿到地址,不能保证能连通。说白了,它就是个问路的人,路能不能走通,还得看NAT的脸色。

8.3 TURN中继服务器部署

当STUN搞不定时,TURN(Traversal Using Relays around NAT)就上场了。它的思路很简单:既然打不通,那就绕路走。所有数据都经过TURN服务器中转。

部署TURN服务器,我推荐用coturn,这是目前最成熟的开源方案。下面是我常用的部署步骤:

# 安装coturn
sudo apt-get install coturn

# 配置turnserver.conf
listening-port=3478
tls-listening-port=5349
fingerprint
lt-cred-mech
user=webrtc:your_password
realm=yourdomain.com
total-quota=100
bps-capacity=0
stale-nonce=600

避坑指南:我曾经在生产环境忘了配置realm,结果客户端一直报认证失败。排查了半天才发现是realm没设置。另外,total-quota和bps-capacity一定要根据服务器带宽合理设置,否则容易被流量打爆。

TURN的部署架构,我画了一张图帮你理解:

TURN中继服务器部署架构 客户端A NAT: 对称型 客户端B NAT: 端口受限型 STUN服务器 TURN中继服务器 端口: 3478 (UDP/TCP) 公网 获取映射地址 获取映射地址 分配中继地址 分配中继地址 中继媒体数据 图例: STUN信令 TURN分配 中继数据流

从图中可以看到,当STUN无法打通时,两个客户端都向TURN服务器申请中继地址。TURN服务器分配好端口后,两边的媒体数据都经过TURN转发。说白了,TURN就是当了个中间人。

性能考量:TURN服务器的带宽消耗是双向的。比如客户端A发1Mbps数据给B,TURN服务器实际消耗2Mbps(收1M,发1M)。我建议按峰值带宽的3倍来规划服务器资源。

部署时还有几个细节:

  • 认证方式:生产环境一定要开启lt-cred-mech,使用长期凭证机制。临时凭证虽然方便,但安全性差。
  • TLS支持:如果业务涉及敏感数据,建议开启TLS(端口5349)。不过要注意,TLS会增加握手延迟。
  • 端口范围:TURN默认使用3478,但中继数据端口是动态分配的。我习惯在配置里固定端口范围,方便防火墙放行。

我的经验:在部署TURN集群时,我会用Keepalived做高可用。两台TURN服务器共享一个虚拟IP,一台挂了另一台自动接管。这样即使单点故障,连接也不会中断。

最后说一句,TURN不是万能的。它解决了连通性问题,但带来了延迟和带宽成本。在实际项目中,我建议先尝试STUN,只有在STUN失败时才回退到TURN。这样既能保证连通率,又能节省服务器资源。


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