# Connection 与连通性检查 · 一个候选对通不通

> 一个 Connection 就是一个候选对——本地候选配远端候选，是 ICE 里「连通性」的最小单元。这一章读 base/connection.h 真实的 WriteState 状态机（WRITABLE/UNRELIABLE/INIT/TIMEOUT）、writable 与 receiving 的区别，以及 Ping 怎么用 STUN 探测把一条路验证成「可用」。

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

# Connection 与连通性检查 · 一个候选对通不通

[候选采集](https://daiw.org/manual/webrtc/port-allocator)好了，ICE 的核心动作登场：把**本地候选**和**远端候选**两两配成 **`Connection`**，逐个验证「这条路到底通不通」。`Connection`（`p2p/base/connection.h`）是 ICE 里「连通性」的**最小单元**，理解它的状态机，你就懂了 ICE 怎么判断一条路能不能用。

## Connection = 一个候选对

一个 `Connection` 对应一个**候选对（candidate pair）**：一个[本地候选](https://daiw.org/manual/webrtc/candidates) + 一个[远端候选](https://daiw.org/manual/webrtc/candidates)。`g3doc/ice.md` 说得精准：它「provides the management of a CandidatePair」——管理这一对候选之间的数据收发，并**发 [STUN Binding 请求（STUN ping）](https://daiw.org/manual/webrtc/stun-protocol)** 来验证包能否一来一回地穿过、并保活。

[`P2PTransportChannel`](https://daiw.org/manual/webrtc/ice-overview) 每收到一个远端候选（[`AddRemoteCandidate`](https://daiw.org/manual/webrtc/ice-overview)），就把它和所有**兼容**的本地候选配对，生成一批 `Connection`（兼容 = 能互发，如 IPv4 配 IPv4、UDP 配 UDP）。于是候选一多，`Connection` 就成批出现——每一个都是一条待验证的「路」。

## WriteState：一条路的四种状态

一个 `Connection` 通不通，用 `WriteState` 描述。`base/connection.h` 里的真实定义，注释就把语义写透了：

```cpp
// p2p/base/connection.h
enum WriteState {
  STATE_WRITABLE = 0,          // 最近收到过 ping 响应(这条路通!)
  STATE_WRITE_UNRELIABLE = 1,  // 有几次 ping 失败(开始不稳)
  STATE_WRITE_INIT = 2,        // 还没收到过 ping 响应(初始,未知)
  STATE_WRITE_TIMEOUT = 3,     // 大量 ping 失败(这条路废了)
};
bool writable() const;    // 是否可写(= STATE_WRITABLE)
bool receiving() const;   // 是否正在收到对端的包
```

<Callout type="info">
  **一条路的一生，就是在这四个状态间迁移。** 刚配好时是 `STATE_WRITE_INIT`（未知）；发 [STUN ping](https://daiw.org/manual/webrtc/stun-protocol) 收到响应，升为 `STATE_WRITABLE`（通了！）；之后若开始丢 ping，降为 `STATE_WRITE_UNRELIABLE`（不稳）；持续失败到一定程度，沦为 `STATE_WRITE_TIMEOUT`（废了，被弃用）。整个连通性检查，就是**用 ping 的成败，不断刷新每条路的 WriteState**。

  注意 `writable()`（能发出去，收到过响应）和 `receiving()`（正在收到对端的包）是**两个独立维度**——ICE 要的是**双向可达**：既能发、又能收。只有 `writable` 的路，可能只是「我发的探测有回应」，还要看 `receiving` 才能确认双向真的活着。这个「发」「收」分开建模，精确对应了真实网络里「上行通不代表下行通」的不对称。
</Callout>

## Ping：检查的心跳

驱动状态迁移的，是 `Connection::Ping()`（`base/connection.h:248`）：它发一个带 [ICE 凭据](https://daiw.org/manual/webrtc/stun-protocol)的 STUN Binding 请求。谁来决定「什么时候、给哪个 `Connection` 发 ping」？是[下一章的 `IceController`](https://daiw.org/manual/webrtc/ice-controller)——它统筹所有 `Connection`，排出 ping 的节奏。

ping 的双重身份（[STUN 那章](https://daiw.org/manual/webrtc/stun-protocol)讲过）：**建连阶段**是「敲门验证」（这条路通不通），**选定之后**是「保活心跳」（维持 [NAT 绑定](https://daiw.org/manual/webrtc/stun-protocol)、确认对端还在）。所以 ping 从不停止——它是 ICE 连接的脉搏。`Connection` 还会据 ping 的往返测出 **RTT（往返时延）**，这是[后面选路](https://daiw.org/manual/webrtc/ice-controller)和[拥塞控制](https://daiw.org/manual/webrtc/source-map)的重要输入。

## 提名：被选中的那条

`Connection` 还有个关键属性 `nominated()`（`base/connection.h:216`）：

```cpp
bool nominated() const;  // 这个候选对是否被「提名」为要用的那条
```

在一堆通了的 `Connection` 里，总要**选一条**来真正传媒体——被选中的那条会被**提名（nominated）**。提名机制（常规提名 vs 激进提名）和「谁有权提名」（[controlling 角色](https://daiw.org/manual/webrtc/nomination-and-restart)），是[下一章](https://daiw.org/manual/webrtc/ice-controller)的主题。这里先记住：`nominated()` 标记着「这就是选中的路」。

## 小结与下一章

**`Connection` 是一个[候选对](https://daiw.org/manual/webrtc/candidates)，ICE 连通性的最小单元**。[`P2PTransportChannel`](https://daiw.org/manual/webrtc/ice-overview) 把兼容的本地 × 远端候选配成一批 `Connection`。每条路的通断用 **`WriteState`** 描述：`INIT`（未知）→ `WRITABLE`（收到 ping 响应，通了）→ `UNRELIABLE`（不稳）→ `TIMEOUT`（废了）；`writable()`（能发）和 `receiving()`（能收）是**两个维度**，ICE 要双向可达。**`Ping()`** 发 [STUN 探测](https://daiw.org/manual/webrtc/stun-protocol)驱动状态、测 RTT、兼做保活。`nominated()` 标记选中的路。

谁在指挥这一切——决定 ping 谁、选哪条、什么时候切换？那是 **`P2PTransportChannel`** 这个总指挥的活。下一章看它怎么把采集、配对、检查、选路串成一台状态机。👉 [P2PTransportChannel：ICE 总指挥](https://daiw.org/manual/webrtc/p2p-transport-channel)。

## 参考资料

- [1] libwebrtc 源码.`p2p/base/connection.h` / `.cc`（`WriteState`、`writable` / `receiving`、`Ping`、`nominated`、RTT）。
- [2] libwebrtc 源码.`g3doc/ice.md`（Connection 管理候选对、发 STUN ping）。
- [3] IETF RFC 8445「ICE」§ Connectivity Checks。
