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

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

作者 David更新于 第 21 篇(共 112 篇)

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

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

Connection = 一个候选对

一个 Connection 对应一个候选对(candidate pair):一个本地候选 + 一个远端候选。g3doc/ice.md 说得精准:它「provides the management of a CandidatePair」——管理这一对候选之间的数据收发,并发 STUN Binding 请求(STUN ping) 来验证包能否一来一回地穿过、并保活。

P2PTransportChannel 每收到一个远端候选(AddRemoteCandidate),就把它和所有兼容的本地候选配对,生成一批 Connection(兼容 = 能互发,如 IPv4 配 IPv4、UDP 配 UDP)。于是候选一多,Connection 就成批出现——每一个都是一条待验证的「路」。

WriteState:一条路的四种状态

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

// 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;   // 是否正在收到对端的包

一条路的一生,就是在这四个状态间迁移。 刚配好时是 STATE_WRITE_INIT(未知);发 STUN ping 收到响应,升为 STATE_WRITABLE(通了!);之后若开始丢 ping,降为 STATE_WRITE_UNRELIABLE(不稳);持续失败到一定程度,沦为 STATE_WRITE_TIMEOUT(废了,被弃用)。整个连通性检查,就是用 ping 的成败,不断刷新每条路的 WriteState。

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

Ping:检查的心跳

驱动状态迁移的,是 Connection::Ping()(base/connection.h:248):它发一个带 ICE 凭据的 STUN Binding 请求。谁来决定「什么时候、给哪个 Connection 发 ping」?是下一章的 IceController——它统筹所有 Connection,排出 ping 的节奏。

ping 的双重身份(STUN 那章讲过):建连阶段是「敲门验证」(这条路通不通),选定之后是「保活心跳」(维持 NAT 绑定、确认对端还在)。所以 ping 从不停止——它是 ICE 连接的脉搏。Connection 还会据 ping 的往返测出 RTT(往返时延),这是后面选路和拥塞控制的重要输入。

提名:被选中的那条

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

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

在一堆通了的 Connection 里,总要选一条来真正传媒体——被选中的那条会被提名(nominated)。提名机制(常规提名 vs 激进提名)和「谁有权提名」(controlling 角色),是下一章的主题。这里先记住:nominated() 标记着「这就是选中的路」。

小结与下一章

Connection 是一个候选对,ICE 连通性的最小单元。P2PTransportChannel 把兼容的本地 × 远端候选配成一批 Connection。每条路的通断用 WriteState 描述:INIT(未知)→ WRITABLE(收到 ping 响应,通了)→ UNRELIABLE(不稳)→ TIMEOUT(废了);writable()(能发)和 receiving()(能收)是两个维度,ICE 要双向可达。Ping() 发 STUN 探测驱动状态、测 RTT、兼做保活。nominated() 标记选中的路。

谁在指挥这一切——决定 ping 谁、选哪条、什么时候切换?那是 P2PTransportChannel 这个总指挥的活。下一章看它怎么把采集、配对、检查、选路串成一台状态机。👉 P2PTransportChannel:ICE 总指挥。

参考资料

  • [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。

本页目录