# P2PTransportChannel · ICE 总指挥

> P2PTransportChannel 是 ICE 的总指挥，把候选采集、配对、连通性检查、选路串成一台状态机。这一章读 base/p2p_transport_channel.h：它如何实现 IceTransportInternal、MaybeStartGathering 与 AddRemoteCandidate 两个入口、以及 gathering 与 transport 两套状态怎么演进到 connected/completed。

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

# P2PTransportChannel · ICE 总指挥

前面几章的零件——[候选](https://daiw.org/manual/webrtc/candidates)、[Port](https://daiw.org/manual/webrtc/port-allocator)、[STUN](https://daiw.org/manual/webrtc/stun-protocol)、[Connection](https://daiw.org/manual/webrtc/connection-and-checks)——都要有个总指挥把它们协同起来。这个总指挥就是 **`P2PTransportChannel`**（`p2p/base/p2p_transport_channel.h`）。它实现 `IceTransportInternal` 接口，是[上一部分 `JsepTransport` 里那个 ICE 传输](https://daiw.org/manual/webrtc/jsep-transport-controller)的真身。这一章看它怎么把 ICE 跑成一台状态机。

## 两个入口

`P2PTransportChannel` 对外的核心动作，是[通话流程](https://daiw.org/manual/webrtc/call-lifecycle)驱动的两个入口（`p2p/base/p2p_transport_channel.h`）：

```cpp
// p2p/base/p2p_transport_channel.h(节选)
void MaybeStartGathering() override;             // 开始采集候选
IceGatheringState gathering_state() const override;
void AddRemoteCandidate(const Candidate& candidate) override;  // 加入远端候选
IceTransportStateInternal GetState() const override;
```

- **`MaybeStartGathering`** —— 由 [`SetLocalDescription`](https://daiw.org/manual/webrtc/offer-answer-handler) 触发，委托 [`PortAllocator`](https://daiw.org/manual/webrtc/port-allocator) 开始采集本地候选。采到的候选经 `SignalCandidateGathered` [trickle](https://daiw.org/manual/webrtc/candidates) 给应用。
- **`AddRemoteCandidate`** —— 收到对端候选时调用，把它和[兼容的本地候选配成 `Connection`](https://daiw.org/manual/webrtc/connection-and-checks)，交给 [`IceController`](https://daiw.org/manual/webrtc/ice-controller)。

一个管「采集与上报本地候选」，一个管「接纳远端候选并配对」——两股候选在 `P2PTransportChannel` 里汇合成一张连接网。

## 两套状态

`P2PTransportChannel` 维护**两套**状态，别混淆：

<Callout type="info">
  **采集状态（gathering）和连接状态（transport）是两件事。**

  - **`IceGatheringState`** —— 本地候选**采集**的进度：`new`（没开始）→ `gathering`（采集中）→ `complete`（采完了）。它只关心「我的候选找齐了没」。
  - **`IceTransportState`**（对应 W3C 的 `RTCIceTransportState`）—— 和对端**连通**的进度：`new` → `checking`（正在[做连通性检查](https://daiw.org/manual/webrtc/connection-and-checks)）→ `connected`（至少一条路通了）→ `completed`（检查全部完成、选定最优）→ 以及出问题时的 `disconnected` / `failed` / `closed`。

  你在 [`PeerConnectionObserver`](https://daiw.org/manual/webrtc/call-lifecycle) 里分别收到 `OnIceGatheringChange` 和 `OnIceConnectionChange` 两组回调，对应的正是这两套状态。**「候选采集完了」不等于「连上了」**——你可能候选早采完，却还在苦苦检查；也可能候选还没采齐，就已经用先到的候选连上了（[trickle](https://daiw.org/manual/webrtc/candidates) 的好处）。分开建模，才能精确反映真实进度。
</Callout>

## 状态怎么演进

把[建连五步](https://daiw.org/manual/webrtc/ice-overview)落到 `P2PTransportChannel` 的状态机上：

```mermaid
flowchart TD
    NEW["new"] -->|"MaybeStartGathering + 有远端候选"| CHK["checking<br/>(配对成 Connection,发 STUN 探测)"]
    CHK -->|"某条 Connection 变 WRITABLE"| CONN["connected<br/>(至少一条路通,可传媒体)"]
    CONN -->|"检查完成 + IceController 选定最优"| COMP["completed"]
    CONN -.->|"选中的路 ping 超时"| DIS["disconnected"]
    DIS -.->|"重新找到可用路"| CONN
    CHK -.->|"所有候选对都失败"| FAIL["failed"]
```

`P2PTransportChannel` 内部有个 `SortConnectionsAndUpdateState`——每当有 `Connection` 状态变化（变 [writable、收到包、RTT 更新](https://daiw.org/manual/webrtc/connection-and-checks)），它就**重新给所有连接排序**、更新自己的 transport 状态、并让 [`IceController`](https://daiw.org/manual/webrtc/ice-controller) 决定是否切换选中的连接。这个「排序 + 更新」是 ICE 动态适应网络的核心循环。

## 触发下游：连上了就握手

`g3doc/ice.md` 的建连第五步：一旦 `P2PTransportChannel` 首次选定一条连接、状态变化，它 `SignalIceTransportStateChanged` 向上通知——这会让上层的 [`DtlsTransport`](https://daiw.org/manual/webrtc/jsep-transport-controller) 意识到「路通了」，**开始 [DTLS 握手](https://daiw.org/manual/webrtc/source-map)**（[第 3 部分](https://daiw.org/manual/webrtc/source-map)）。至此 ICE 完成使命：它交出一条**能通的、加密前的**通路，媒体的加密与传输接力开始。

## 小结与下一章

**`P2PTransportChannel` 是 ICE 总指挥**（实现 `IceTransportInternal`），两个入口：**`MaybeStartGathering`**（采集本地候选）和 **`AddRemoteCandidate`**（接纳远端候选并[配对成 `Connection`](https://daiw.org/manual/webrtc/connection-and-checks)）。它维护**两套状态**——采集（`new/gathering/complete`）与连接（`new/checking/connected/completed/failed/disconnected`），分别对应 `OnIceGatheringChange` / `OnIceConnectionChange`。核心循环 `SortConnectionsAndUpdateState` 随连接变化重排、更新状态、驱动选路。连上后 `SignalIceTransportStateChanged` 触发 [DTLS 握手](https://daiw.org/manual/webrtc/source-map)。

它把「选哪条连接用、什么时候切」委托给了 **`IceController`**。下一章读这个选路大脑，以及 controlling / controlled 角色。👉 [IceController：选路的大脑](https://daiw.org/manual/webrtc/ice-controller)。

## 参考资料

- [1] libwebrtc 源码.`p2p/base/p2p_transport_channel.h` / `.cc`（`MaybeStartGathering`、`AddRemoteCandidate`、`GetState`、`SortConnectionsAndUpdateState`）。
- [2] libwebrtc 源码.`p2p/base/ice_transport_internal.h`（`IceGatheringState` / `IceTransportState`）、`g3doc/ice.md`。
- [3] IETF RFC 8445「ICE」；W3C `RTCIceTransportState`。
