STUN/TURN服务器必须使用成熟实现如coturn,不可手写核心协议;coturn需显式配置relay-address、端口池、认证及realm,否则无法中继;WebRTC客户端需正确配置turn:或turns: URL、凭据及transport类型,否则无relay candidate或连接失败。

STUN/TURN 服务器不能“自己写核心”,得用成熟实现
你不需要、也不应该从零实现 STUN/TURN 协议逻辑——RFC 5389(STUN)、RFC 5766(TURN)、RFC 8445(ICE)定义了大量边界行为、事务模型、完整性校验和安全机制,手写极易出错,且无法通过标准兼容性测试。实际部署中,coTURN 是唯一被广泛验证、持续维护、支持完整 TURN 功能(UDP/TCP/TLS over TCP/TLS over DTLS)的开源方案。它的 turnserver 进程本身就是 ICE 协商所需的中继核心。
coTURN 配置必须显式启用 relay 并隔离权限
默认配置下 coTURN 只响应 STUN 绑定请求,不分配中继地址。要让它真正“中继”,需确认以下几项已设为非注释状态:
-
listening-port=3478和tls-listening-port=5349(如需 TLS)必须开放且防火墙放行 -
relay-address=0.0.0.0(或指定公网 IP),否则中继 socket 绑定失败 -
min-port和max-port(如min-port=49152,max-port=65535)必须设置,TURN 中继靠端口池分配 -
use-auth-secret+static-auth-secret=your_secret:强制使用时间戳+HMAC 认证,避免 open relay 被滥用 -
realm=example.com:必须设置,否则某些客户端(如 WebRTC 浏览器)拒绝使用该 TURN
漏掉 relay-address 或端口范围,会导致客户端收到 403 Forbidden 或 ALLOCATE 请求超时。
WebRTC 客户端连不上?检查 ICE candidate 类型和 transport
浏览器调用 RTCPeerConnection 时,若只配了 STUN,iceCandidate 事件里永远不会有 relay 类型;只有配置了 TURN(且认证通过),才会出现 candidate:... typ relay ...。常见错误:
立即学习“C++免费学习笔记(深入)”;
- 在
iceServers数组里把 STUN 和 TURN 写成两个独立对象,但 TURN 的urls缺少turn:前缀(应为"turn:your-server:3478?transport=udp") - 没传
username和credential(WebRTC 会自动用createTurnCredentials()生成临时凭据,但前提是服务端启用了use-auth-secret) - 客户端走 HTTPS 页面却用
turn:(非 TLS),被浏览器策略阻止;此时必须用turns:+tls-listening-port+ 有效证书
ICE 连通性检查失败?别怪服务器,先看 NAT 类型和对称性
coTURN 本身不解决“P2P 打洞失败”的根本问题。它只是 fallback:当 host 和 srflx candidate 全部不可达时,才用 relay。如果连 relay 都不通,大概率是:
- 服务器公网 IP 没正确填进
external-ip配置项(NAT 后的机器必须显式告知外网可见 IP,否则客户端收到的 relay candidate 地址是内网地址) - 云厂商(阿里云/腾讯云)安全组未放开 UDP 端口段(不只是 3478,更要放开
min-port~max-port全部 UDP) - 客户端在企业级对称 NAT 下,即使有 relay,也因 UDP 回包路径不对称而丢包——这时只能切 TCP relay(coTURN 支持,但需客户端明确请求
?transport=tcp)
真要调试,用 tcpdump -i any port 3478 or portrange 49152-65535 看包是否到达服务器,比查 C++ 日志快得多。


















