6、STUN/TURN服务器:STUN原理、TURN原理、部署coturn服务器、配置ICE服务器列表
屏幕共享这个功能,说白了就是要把你电脑上的一帧帧画面,实时传到对方那里去。但互联网不是直通的,中间隔着路由器、防火墙、NAT(网络地址转换)。你想想看,你家电脑的IP是192.168.x.x,这是个内网地址,外网根本找不到你。那怎么把视频流送过去?
嗯,这就是我们今天要聊的核心——STUN和TURN。我刚开始做WebRTC的时候,觉得这玩意儿就是个配置项,填个地址就行。后来线上出问题了,用户反馈“屏幕共享一直转圈”,我才老老实实把原理啃了一遍。今天我把这些经验掰开揉碎了讲给你。
6.1 STUN原理:帮你找到自己的“外网门牌号”
STUN的全称是Session Traversal Utilities for NAT,翻译过来就是“NAT会话穿越工具”。它的作用很简单:帮你找到你的公网IP和端口。
流程是这样的:
- 你的浏览器向STUN服务器发一个请求:“喂,我现在的公网地址是多少?”
- STUN服务器看到这个请求的来源IP和端口,记录下来,然后原样返回给你。
- 浏览器拿到这个地址,把它塞进SDP(会话描述协议)里,发给对方。
对方收到你的公网地址后,尝试直接连接。如果双方都在简单的NAT后面(比如常见的锥形NAT),这条路就走通了。这就是P2P直连。
核心要点:STUN只负责“查地址”,不负责“传数据”。它是个轻量级的服务,成本很低。
我在项目中遇到过一种情况:用户在公司内网,网络环境特别复杂。STUN查到的地址是对的,但对方就是连不上。为什么?因为公司防火墙把UDP端口封了,或者NAT类型是“对称型NAT”。这时候STUN就无能为力了。
避坑指南:我曾经以为STUN能解决所有NAT问题,结果被对称型NAT狠狠教育了一顿。对称型NAT每次连接都分配不同的端口,STUN查到的地址只对本次查询有效,对方再连过来时端口已经变了。所以,STUN不是万能的。
6.2 TURN原理:当直连走不通时,我来当中转
TURN的全称是Traversal Using Relays around NAT,翻译过来是“使用中继穿越NAT”。说白了,就是当P2P走不通时,让服务器帮你转发数据。
流程是这样的:
- 浏览器先跟TURN服务器建立连接,服务器分配一个中继地址(公网IP+端口)。
- 浏览器把这个中继地址放进SDP,发给对方。
- 对方连接这个中继地址,所有数据都经过TURN服务器转发。
你想想看,这相当于在公网上找了个“中间人”,你俩都连到它,它帮你传数据。代价是什么?带宽和延迟。TURN服务器要消耗大量的网络流量,而且多了一跳,延迟肯定比直连高。
重要提醒:TURN服务器是成本大头。我见过一个项目,每天跑几百GB的流量,光服务器带宽费就上万。所以,能用STUN直连的,千万别用TURN。ICE(交互式连接建立)协议会自动帮你选最优路径。
ICE的决策逻辑是这样的:先尝试P2P(STUN拿到的地址),如果连不上,再尝试TURN中继。ICE会收集所有候选地址(candidate),然后排序,优先用延迟最低的那个。
6.3 部署coturn服务器:手把手教你搭一个
coturn是目前最流行的开源STUN/TURN服务器。我部署过好几次,踩过不少坑,这里给你一个稳的步骤。
6.3.1 安装coturn
以Ubuntu为例,直接apt安装:
sudo apt update
sudo apt install coturn
安装完成后,默认配置文件在 /etc/turnserver.conf。我个人习惯先备份原文件:
sudo cp /etc/turnserver.conf /etc/turnserver.conf.bak
6.3.2 配置核心参数
编辑配置文件,以下是我常用的配置:
# 监听端口
listening-port=3478
# TLS端口(可选)
tls-listening-port=5349
# 外网IP(必填!填你的服务器公网IP)
external-ip=你的公网IP
# 内网IP(如果服务器有内网IP,填上)
relay-ip=你的内网IP
# 用户认证(强烈建议开启)
fingerprint
lt-cred-mech
user=webrtc:your_password
# 日志
verbose
log-file=/var/log/turnserver.log
# 进程数
proc-worker=4
关键点:external-ip必须填对。我曾经忘了填这个,结果客户端拿到的中继地址是内网IP,对方根本连不上。排查了半天才发现是这个问题。
6.3.3 启动服务
sudo systemctl enable coturn
sudo systemctl start coturn
sudo systemctl status coturn
检查是否正常运行:
sudo netstat -tulpn | grep 3478
如果看到 LISTEN 状态,说明服务起来了。
6.3.4 防火墙放行
别忘了开防火墙端口:
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp
sudo ufw allow 5349/tcp
sudo ufw allow 5349/udp
嗯,这里要注意,TURN还需要开放一段UDP端口用于中继数据。默认是 49152-65535,你可以在配置里用 min-port 和 max-port 限制范围。
6.4 配置ICE服务器列表:让WebRTC知道该找谁
服务器搭好了,怎么让WebRTC用上它?很简单,在创建 RTCPeerConnection 时传入ICE服务器列表。
const iceServers = [
{
urls: 'stun:你的服务器IP:3478'
},
{
urls: 'turn:你的服务器IP:3478',
username: 'webrtc',
credential: 'your_password'
}
];
const pc = new RTCPeerConnection({ iceServers });
这里有几个细节:
- STUN可以不加认证,因为只是查地址,不涉及数据转发。但TURN必须加认证,否则谁都能用你的服务器转发数据,流量会爆。
- 可以配置多个STUN/TURN地址,ICE会逐个尝试。我一般配两个STUN(一个自己的,一个Google的公共STUN),再加一个自己的TURN。
- Google的公共STUN地址:
stun:stun.l.google.com:19302,开发测试时可以用,但生产环境建议用自己的。
我的经验:配置ICE服务器列表时,把STUN放在前面,TURN放在后面。因为ICE会按顺序尝试,STUN优先。如果STUN成功了,就不会去连TURN,省带宽。
6.5 知识体系总览
下面这张图帮你理清STUN、TURN、ICE之间的关系:
6.6 常见问题与排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| STUN查不到地址 | 防火墙封了UDP 3478端口 | 检查防火墙规则,或改用TCP |
| TURN连接失败 | 用户名密码错误,或external-ip没配 | 检查配置文件,重启coturn |
| 屏幕共享卡顿 | 走了TURN中继,延迟高 | 优化网络,或升级TURN服务器带宽 |
| ICE一直处于checking状态 | 所有候选地址都连不上 | 检查STUN/TURN是否可达,看日志 |
最后提醒一句:部署TURN服务器时,一定要做好流量监控。我曾经没设流量上限,结果一个月跑了2TB,账单吓死人。建议在coturn配置里加上 total-quota=100(限制总带宽)和 bps-capacity=0(不限制单用户),或者用云平台的流量告警。
好了,STUN/TURN这块就聊到这。你按这个步骤去搭,基本不会出大问题。如果遇到奇怪的网络环境,记得先看coturn的日志,里面会告诉你为什么连接失败。