# TURN 中继 · 直连不通时的保底

> 对称 NAT 会让 STUN 直连失败，这时只能让媒体经服务器转发——这就是 TURN。这一章读 base/turn_port.h，讲清 TURN 的 Allocate 分配中继地址、Permission 授权机制、媒体怎么绕道服务器，以及为什么它虽是保底方案却不可或缺。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/turn
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# TURN 中继 · 直连不通时的保底

[STUN](https://daiw.org/manual/webrtc/stun-protocol) 能让大多数端点穿越 NAT 直连，但**不是全部**。遇到「对称 NAT」或严格防火墙，srflx 候选也会失败——两端怎么都直连不上。这时 ICE 还有最后一张牌：**TURN 中继**。它是[四种候选](https://daiw.org/manual/webrtc/candidates)里最兜底的 `relay` 的来源。

## 为什么需要 TURN

<Callout type="info">
  **对称 NAT 让 STUN 失效：它为每个不同的目标，分配不同的公网端口。** 你[问 STUN 服务器](https://daiw.org/manual/webrtc/stun-protocol)得到的公网地址（端口 A），只在「你 → STUN 服务器」这条路上有效；当对端试图用端口 A 连你时，因为对端是**不同的目标**，对称 NAT 根本没有对应的映射，包被丢弃。于是 srflx 候选形同虚设。据统计，真实互联网上有相当比例的连接（常见说法约一到两成）最终**必须**靠中继才能建立。

  出路是：**别再尝试直连，让媒体经一台双方都连得上的公共服务器转发。** 这台服务器就是 **TURN（Traversal Using Relays around NAT）** 服务器。两端各自连上 TURN，媒体走「我 → TURN → 对端」，绕开了 NAT 的直连难题。代价显而易见：多一跳、加延迟、耗服务器带宽——所以它是**保底**，只在直连全败时才用。
</Callout>

## TurnPort：申请一个中继地址

采集 relay 候选的，是 [`TurnPort`](https://daiw.org/manual/webrtc/port-allocator)（`p2p/base/turn_port.h`）。它的核心动作是向 TURN 服务器发一个 **Allocate 请求**，请服务器**分配一个中继地址**给自己：

```cpp
// p2p/base/turn_port.h(节选)
class TurnPort : public Port {
  Connection* CreateConnection(const Candidate& c, ...) override;
  // 内部:TurnAllocateRequest(申请中继地址)、TurnEntry(每个对端一条)
};
```

流程：`TurnPort` 向 TURN 服务器认证并发 Allocate → 服务器在自己身上开一个端口（中继地址）分配给你 → 这个中继地址就成了你的 **relay [候选](https://daiw.org/manual/webrtc/candidates)**，经[信令](https://daiw.org/manual/webrtc/call-lifecycle)发给对端。对端把包发到这个中继地址，TURN 服务器就转发给你；反之亦然。**TURN 服务器成了你在公网上的一个『信箱』**，谁都能往里投递，它再转交给藏在 NAT 后的你。

## Permission：不是谁都能中继

TURN 服务器要转发媒体，但**不能变成任人滥用的开放代理**。所以它有**授权机制**：你必须**明确告诉 TURN 服务器「允许来自对端地址 X 的包转发给我」**，它才会转。`turn_port.h` 里的 `TurnEntry` 和 `OnTurnCreatePermissionResult` 就是干这个——为每个要通信的对端，向 TURN 服务器 **CreatePermission（创建权限）**。没有权限，对端的包会被 TURN 丢弃。这既防滥用，也是 TURN 认证体系（长期凭据）的一部分。

## 媒体怎么绕道

一旦选中一条走 relay 的路，媒体的旅程就变成：

```mermaid
flowchart LR
    A["端点 A(NAT 后)"] -->|"包裹在 TURN 消息里"| T["TURN 服务器(公网)"]
    T -->|"转发"| B["端点 B(NAT 后)"]
    B -->|"回程同理"| T
    T --> A
```

注意：媒体在 A↔TURN 段是**包裹在 TURN 协议里**的（TURN 用 Send/Data 指示或 Channel 通道承载真实数据），TURN→B 段再拆出来转发。整条路径上，[SRTP 加密](https://daiw.org/manual/webrtc/source-map)依然端到端有效——**TURN 服务器只转发密文，看不到媒体内容**（[第 3 部分](https://daiw.org/manual/webrtc/source-map)），这是 WebRTC [强制加密](https://daiw.org/manual/webrtc/what-is-webrtc)带来的安全保证：哪怕媒体过了第三方服务器，隐私也不泄露。

TURN 还能走不同传输：TURN-over-UDP（默认）、TURN-over-TCP、乃至 **TURN-over-TLS**（443 端口，伪装成 HTTPS，穿越最严格的企业防火墙）——层层退路，确保「总有一条能通」。

## 小结与下一章

**TURN 是直连全败时的保底**：对称 NAT 让 [STUN](https://daiw.org/manual/webrtc/stun-protocol) 直连失效，只能让媒体**经公共 TURN 服务器转发**。[`TurnPort`](https://daiw.org/manual/webrtc/port-allocator) 向服务器发 **Allocate** 申请一个中继地址（即 [relay 候选](https://daiw.org/manual/webrtc/candidates)），并为每个对端 **CreatePermission** 授权（防滥用）。媒体绕道「A → TURN → B」，但 [SRTP](https://daiw.org/manual/webrtc/source-map) 端到端加密，TURN 只见密文。它多一跳、费带宽，是最后手段，但**不可或缺**——没有它，一到两成的连接建不起来。TURN 还支持 UDP/TCP/TLS 多种传输作退路。

候选和采集都齐了，该看 ICE 的**核心动作**了：把本地和远端候选配成 **`Connection`**，发 [STUN 探测](https://daiw.org/manual/webrtc/stun-protocol)验证它通不通。下一章读它。👉 [Connection 与连通性检查](https://daiw.org/manual/webrtc/connection-and-checks)。

## 参考资料

- [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）」。
