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和端口

流程是这样的:

  1. 你的浏览器向STUN服务器发一个请求:“喂,我现在的公网地址是多少?”
  2. STUN服务器看到这个请求的来源IP和端口,记录下来,然后原样返回给你。
  3. 浏览器拿到这个地址,把它塞进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走不通时,让服务器帮你转发数据

流程是这样的:

  1. 浏览器先跟TURN服务器建立连接,服务器分配一个中继地址(公网IP+端口)。
  2. 浏览器把这个中继地址放进SDP,发给对方。
  3. 对方连接这个中继地址,所有数据都经过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-portmax-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之间的关系:

WebRTC ICE连接决策流程 浏览器A (发起屏幕共享) 浏览器B (接收屏幕共享) STUN服务器 查公网地址 TURN服务器 中继转发数据 ICE决策 ① 查地址 返回公网地址 ② 中继地址 分配中继地址 P2P直连(优先) TURN中继(备选) ICE优先尝试P2P直连(STUN),失败后自动回退到TURN中继

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的日志,里面会告诉你为什么连接失败。


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