根本原因是nat穿透失败:跨网络时双方仅报告内网地址,stun未生效或turn未配置/配置错误,导致ice候选缺失srflx/relay类型,无法建立连接。

为什么本地跑通但跨网络就黑屏或 connecting 卡住
根本原因不是代码写错,而是 WebRTC 在跨 NAT 环境下压根收不到对方的 iceCandidate,或者收到后连不通——因为双方报告的都是内网地址(如 192.168.1.100),STUN 没生效,TURN 没配或配错了。
- 浏览器控制台里出现
ICE connection state is checking却长期不变成connected,基本可判定是 ICE 候选者没打通 - 用
chrome://webrtc-internals查看getStats()输出,如果transport下只有host类型 candidate,没有srflx(STUN)或relay(TURN),说明 STUN/TURN 服务器根本没响应 - 常见误操作:只配了 STUN,却在对称型 NAT 或企业防火墙后运行——这种环境下 STUN 必然失败,必须靠 TURN
VSCode 开发时怎么让 STUN/TURN 配置真正生效
VSCode 本身不参与网络配置,但你写的 JS 代码如果没正确注入 iceServers,或者被开发服务干扰,就会白配。
- 确保
RTCPeerConnection实例化时传入了完整配置,不是空对象:new RTCPeerConnection({ iceServers: [...] }) - 不要把
iceServers写死在前端代码里(尤其含 TURN 凭据),开发阶段可用环境变量注入,生产环境走后端下发 - 用
serve -s . -p 8080启动本地服务(而非 Live Server 的 HTTP),否则 Chrome 会拒绝调用getUserMedia,导致整个连接流程无法触发 - 检查是否启用了 HTTPS:若用自签名证书(如
mkcert),需确认证书已信任且服务确实走https://localhost:8080,否则部分浏览器会屏蔽 STUN/TURN 请求
coturn 配置里最容易漏掉的三项
coturn 是最常用的 TURN 服务实现,但默认配置几乎必然失败——它不会自动暴露公网能力,必须手动“说清楚”。
-
listening-ip要设为服务器实际监听的内网 IP(如192.168.1.10),不是0.0.0.0;同时必须配external-ip指向你的公网 IP 或域名,否则客户端拿到的 relay 地址是错的 -
realm必须显式设置(如realm=yourdomain.com),否则 coturn 会拒绝带 credential 的请求,报错401 Unauthorized - UDP 和 TCP 端口都要开:coturn 默认只开 UDP 的
3478,但很多企业防火墙会封 UDP,务必加listening-port=3478和tls-listening-port=5349,并在防火墙放行对应端口
如何快速验证 STUN/TURN 是否真在工作
别等跑完完整信令流程再排查——先单独测通路。
- 用浏览器访问
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/,填入你的iceServers配置,点 “Gather candidates”。能看到srflx和relay类型 candidate 才算 STUN/TURN 响应正常 - 在服务器上抓包验证:
sudo tcpdump -i any port 3478,然后在页面点击 Gather,应看到 UDP 包进出;没流量说明请求根本没到服务器(可能是反代拦截、端口未映射、防火墙丢包) - 检查 coturn 日志:
sudo journalctl -u coturn -f,成功认证会打印session created,失败则有auth error或no valid realm
external-ip 和 realm 这两个字段漏配比密码写错还常见;而开发者往往花半天查信令逻辑,其实问题早在 coturn 启动那一刻就埋下了。











