TURN 中继 · 直连不通时的保底
对称 NAT 会让 STUN 直连失败,这时只能让媒体经服务器转发——这就是 TURN。这一章读 base/turn_port.h,讲清 TURN 的 Allocate 分配中继地址、Permission 授权机制、媒体怎么绕道服务器,以及为什么它虽是保底方案却不可或缺。
TURN 中继 · 直连不通时的保底
STUN 能让大多数端点穿越 NAT 直连,但不是全部。遇到「对称 NAT」或严格防火墙,srflx 候选也会失败——两端怎么都直连不上。这时 ICE 还有最后一张牌:TURN 中继。它是四种候选里最兜底的 relay 的来源。
为什么需要 TURN
对称 NAT 让 STUN 失效:它为每个不同的目标,分配不同的公网端口。 你问 STUN 服务器得到的公网地址(端口 A),只在「你 → STUN 服务器」这条路上有效;当对端试图用端口 A 连你时,因为对端是不同的目标,对称 NAT 根本没有对应的映射,包被丢弃。于是 srflx 候选形同虚设。据统计,真实互联网上有相当比例的连接(常见说法约一到两成)最终必须靠中继才能建立。
出路是:别再尝试直连,让媒体经一台双方都连得上的公共服务器转发。 这台服务器就是 TURN(Traversal Using Relays around NAT) 服务器。两端各自连上 TURN,媒体走「我 → TURN → 对端」,绕开了 NAT 的直连难题。代价显而易见:多一跳、加延迟、耗服务器带宽——所以它是保底,只在直连全败时才用。
TurnPort:申请一个中继地址
采集 relay 候选的,是 TurnPort(p2p/base/turn_port.h)。它的核心动作是向 TURN 服务器发一个 Allocate 请求,请服务器分配一个中继地址给自己:
// p2p/base/turn_port.h(节选)
class TurnPort : public Port {
Connection* CreateConnection(const Candidate& c, ...) override;
// 内部:TurnAllocateRequest(申请中继地址)、TurnEntry(每个对端一条)
};流程:TurnPort 向 TURN 服务器认证并发 Allocate → 服务器在自己身上开一个端口(中继地址)分配给你 → 这个中继地址就成了你的 relay 候选,经信令发给对端。对端把包发到这个中继地址,TURN 服务器就转发给你;反之亦然。TURN 服务器成了你在公网上的一个『信箱』,谁都能往里投递,它再转交给藏在 NAT 后的你。
Permission:不是谁都能中继
TURN 服务器要转发媒体,但不能变成任人滥用的开放代理。所以它有授权机制:你必须明确告诉 TURN 服务器「允许来自对端地址 X 的包转发给我」,它才会转。turn_port.h 里的 TurnEntry 和 OnTurnCreatePermissionResult 就是干这个——为每个要通信的对端,向 TURN 服务器 CreatePermission(创建权限)。没有权限,对端的包会被 TURN 丢弃。这既防滥用,也是 TURN 认证体系(长期凭据)的一部分。
媒体怎么绕道
一旦选中一条走 relay 的路,媒体的旅程就变成:
注意:媒体在 A↔TURN 段是包裹在 TURN 协议里的(TURN 用 Send/Data 指示或 Channel 通道承载真实数据),TURN→B 段再拆出来转发。整条路径上,SRTP 加密依然端到端有效——TURN 服务器只转发密文,看不到媒体内容(第 3 部分),这是 WebRTC 强制加密带来的安全保证:哪怕媒体过了第三方服务器,隐私也不泄露。
TURN 还能走不同传输:TURN-over-UDP(默认)、TURN-over-TCP、乃至 TURN-over-TLS(443 端口,伪装成 HTTPS,穿越最严格的企业防火墙)——层层退路,确保「总有一条能通」。
小结与下一章
TURN 是直连全败时的保底:对称 NAT 让 STUN 直连失效,只能让媒体经公共 TURN 服务器转发。TurnPort 向服务器发 Allocate 申请一个中继地址(即 relay 候选),并为每个对端 CreatePermission 授权(防滥用)。媒体绕道「A → TURN → B」,但 SRTP 端到端加密,TURN 只见密文。它多一跳、费带宽,是最后手段,但不可或缺——没有它,一到两成的连接建不起来。TURN 还支持 UDP/TCP/TLS 多种传输作退路。
候选和采集都齐了,该看 ICE 的核心动作了:把本地和远端候选配成 Connection,发 STUN 探测验证它通不通。下一章读它。👉 Connection 与连通性检查。
参考资料
- [1] libwebrtc 源码.
p2p/base/turn_port.h/.cc(TurnPort、TurnAllocateRequest、TurnEntry、权限)。 - [2] libwebrtc 源码.
g3doc/ice.md(relay 候选)、api/candidate.h(kRelay)。 - [3] IETF RFC 8656「Traversal Using Relays around NAT (TURN)」。