摘要

  • RFC 8445 先把本地与远端候选组合成候选对,再用 STUN 连通性检查得到有效候选对,最后由控制方 ICE Agent 提名其中一对。所需组件都有已提名候选对后,它们才成为已选候选对;这是一项传输路径决定,不是整场会话的成功证明。
  • RFC 7675 的持续同意只覆盖一个五元组,并且必须不断更新;RFC 8838 的候选结束信号只表示本轮候选输入关闭。身份、授权、媒体处理和应用结果仍要由各自机制给出证据。

一次故障复盘只留下三行时间戳:candidate pair succeeded、nominated、selected。监控系统据此判定“连接成功”,客服记录却说双方始终听不到声音。

如果只允许一个状态是真实的,这两份记录必有一份错误。但 ICE 从未承诺替整条应用链作证。它解决的是一个更具体的问题:在若干可能的传输地址组合中,哪些经过了协议检查,控制方最终选择了哪一对,后续这个组件应当使用哪条路径。

声道是否建立、密钥协商是否完成、包是否成功解密、编解码器是否产出可播放内容、参与者是否获准进入、用户是否完成想做的事,都发生在这项决定之外。把它们压成一个 connected,不是简化事实,而是消除责任边界。

RFC 8445 的作者是 Ari Keränen、Christer Holmberg 与 Jonathan Rosenberg,Keränen 排在首位。这一作者顺序证明他参与了这份 IETF 标准轨文档的集体写作,却不意味着独自发明了所有相关机制,更不意味着他控制任何产品的实现或某次会话的结果。2026 年 8 月 30 日查阅的 IETF 公开资料列出他参与的 21 份 RFC,以及在 T2TRG、Internet of Things Directorate 和 IRSG 中的现任职责;这些角色会变化,因此只能作为有日期的职业背景。

这篇文章关注的不是“ICE 是否可靠”,而是它的证据为什么应当被准确使用。

候选只是一份可供考虑的地址清单

RFC 8445 所说的 candidate,是一个可能用来接收数据的传输地址。它可能来自本机接口,可能是经过 NAT 后由 STUN 观察到的 server-reflexive 地址,也可能是 TURN 分配的 relay 地址。收集候选的动作扩大了可选集合,却没有验证其中任何一项能到达对端。

一个本地候选与一个远端候选组合成 candidate pair。此时的“对”仍是一项待检验命题:从这个本地地址,能不能抵达那个远端地址?优先级只决定先试谁、偏好谁。它可以反映直连优先于中继等选择,却不能把偏好变成连通事实。

候选数量也不是路径数量。十个远端候选不等于十条可用路径;一个 relay 候选不等于会话最终使用 TURN;一个从 NAT 外侧看见的地址更不等于设备身份、账号归属或机构授权。

因此,候选收据至少要保留 ICE generation、所属组件、候选类型、传输地址、base 或 related address、优先级、发现来源与到达时间。ICE restart 会开启新的 generation。若把重启前后的候选混在同一清单里,系统看似掌握更多地址,实际上已经无法还原决策当时真正存在的选择集。

检查成功,只把候选对送入有效集合

两端 ICE Agent 根据候选对建立 checklist,再逐项执行 connectivity check。一次检查是从本地候选向远端候选发出的 STUN Binding 请求与响应事务。它使用的 IP 地址和端口与后续数据相同,因此成功事务是一张很有价值的路径收据。

但收据上的对象是这次 STUN 交换。成功后,一个或多个候选对进入 valid list;对端收到请求后还可能安排 triggered check,加快双向确认;响应观察到的新映射也可能产生 peer-reflexive candidate。

有效不等于已选。清单中可以同时有多对有效候选,控制方可能仍在等待优先级更高的选项,也可能依据本地策略提前结束等待。连通性检查回答“这对地址按 ICE 规则完成了检查吗”,而不是“最终必须使用它吗”。

同样,它不能替更高层作证。STUN 请求能够往返,不代表 DTLS 已完成,不代表 SRTP 包已认证或解密,不代表音视频进入 jitter buffer 后成功解码,也不代表应用接受了参与者。协议层之间存在依赖,但依赖不等于证据继承。

如果日志只保留 ice_connected=true,没有候选、检查超时、已有有效对但尚未提名、选中后密钥失败、媒体到达后无法渲染,都会变成同一种“连接问题”。一个布尔值省掉了字段,也省掉了判断谁应当修复的能力。

提名是控制方依据本地策略作出的决定

ICE 为一次会话确定 controlling 与 controlled 两个角色。控制方负责最终候选对的选择;受控方等待它提名。控制方先让检查继续,直到满足自己的停止条件,再从 valid list 中挑出一对,以带有提名含义的检查事务告知对端。

RFC 8445 要求它最终为每个组件只提名一对,却没有规定所有产品必须采用同一套停止与评价标准。这正是运行策略所在:产品可以等待直连路径,以降低中继成本;也可以为了缩短建立时间,尽早接受已经有效的 relay 路径;还可以根据网络类型、隐私与资源约束调整优先级。

受控方收到提名后,在需要时检查同一候选对;相关事务成功后,两端设置 nominated 标记。所需组件都有已提名候选对时,它们成为 selected pairs,随后用于该数据流组件的发送与接收。

“控制方”只是 ICE 状态机中的协议角色,不是组织治理意义上的上级。提名也不是人的同意、账号授权或合同认可。它只表明:在这次 ICE generation 中,被赋予选择职责的 Agent,基于当时有效集合与本地策略,决定使用这对传输地址。

一份可审计记录应保存谁是控制方、角色冲突如何解决、决策时 valid list 有哪些候选对、策略版本是什么、哪笔事务完成提名、何时安装已选候选对。只记赢家而删除备选集,就无法回答它为何获胜。

数据未必等到“已选”之后才出现

把状态画成一条过于整齐的流水线,还会漏掉 RFC 8445 的一项重要边界:在数据流尚未产生 selected pairs 之前,与某组件关联的任意 valid pair 已经可以承载数据。因此,已选候选对不必是最早出现应用包的路径。

这意味着两种时间关系都不能靠猜。看见早期数据,不能反推出最终候选对已经选定;看见 selected 事件,也不能反推出随后一定出现有用数据。诊断时间线应当能够记录临时使用的有效对、提名事务、最终选择、restart 后的路径变化,以及每条路径上实际发生的应用事件。

“媒体已流动”本身也过于宽泛。发出包不等于对端收到;收到不等于认证;认证不等于解密;解密不等于解码;解码不等于播放;播放不等于人真正听清;人听清也不等于业务操作成功。每一步都有不同的观测点和责任人。

让 selected pair 只证明路径选择,不会削弱它。相反,它会成为故障定位的可靠锚点:这一步已经完成,下一张缺失的收据在哪里?

持续同意只属于一个五元组

路径被选中,不代表发送许可永久存在。RFC 7675 以新的 STUN 请求—响应事务定义 consent freshness。该文作者是 Matthew Perumal、Dan Wing、Rohan Ravindranath、Tirumaleswar Reddy 与 Martin Thomson,不包括 Ari Keränen;在这里引用它,是为了补全后续运行边界,而不是转移作者归属。

持续同意证明远端仍允许向一个具体五元组发送非 ICE 流量。规范明确把它称为 application-level consent,并说明不涉及人的介入。因此,它既不是用户点击“同意”,也不是覆盖所有候选对、所有媒体与整场会话的通行证。

仅仅保持 NAT 映射还不够。send-and-forget 的 STUN indication 可以当 keepalive,却没有响应,不能证明远端仍然同意。持续同意要求匹配且经过认证的 STUN Binding 响应。若在期限内没有得到有效响应,端点必须停止在该五元组上发送,并在恢复前重新取得同意;过期后迟到的响应不能把已经失效的时段改写成连续授权。

RFC 7675 不规定应用如何反应。产品可以启动 ICE restart、显示重连、结束通话或保留本地状态。持续同意的丢失只回答“为什么不能继续向这条五元组发送”,不能诊断编解码器,也不能证明对端的人已经离开。

所以监控中应分别保存 selected_pair_at、五元组、consent_last_confirmed_at、consent_expires_at 与应用状态。路径变化时,五元组也会变化。一个会话级 consent=true 很容易让新路径在记录里继承旧路径从未授予它的许可。

候选结束表示输入关闭,不表示选路完成

RFC 8838 由 Emil Ivov、Justin Uberti 与 Philipp Hancke 撰写。它允许候选逐步到达,使检查在收集全部完成前就能开始。这能降低等待时间,却引入一个新问题:暂时没有新候选,究竟意味着仍在收集,还是本轮已经结束?

end-of-candidates indication 为特定 generation 与数据流关闭这项输入。Agent 表示候选收集已完成,或者等待已超过可接受时长,不会再向当前 ICE session 提供新候选。发送结束信号后,如要加入新的候选集合,就必须 ICE restart。

关闭输入可以帮助状态机更快作出判断:没有有效候选对时,可以确认 checklist 失败;只有较低偏好的 relay 有效时,也不必再无限等待更优候选。

但“没有更多候选”不是“已经选中候选”。RFC 8838 明确保留 RFC 8445 的提名语义;即使收到结束信号,控制方仍需完成提名。反过来,已有已提名候选对时,ICE 也可能在所有结束信号到齐前结束。

候选结束是输入集合的闭包,提名是控制方的选择,selected pair 是被安装的结果,consent freshness 是一条五元组上的持续许可,媒体与应用结果则来自更后的系统。把这些动词分开,才可能把一次复杂会话交还给真正负责的人。

对 Ari Keränen 的归属也应保持同样精度

RFC 8445 把 Keränen 列为三位作者之首,这是他参与这份集体标准工作的直接证据。公开资料中的 21 份 RFC 与现任职责,为这项贡献提供了时间明确的职业背景。它们不应被扩写成对所有 ICE 实现的所有权,也不能把 RFC 7675 和 RFC 8838 的作者工作算到他名下。

作者、共同作者、IETF 共识、产品代码、网络运营者与会话用户,彼此不是同一个角色。候选、有效候选对、已提名候选对、已选候选对、持续同意与应用成功,也不是同一个状态。

那对候选赢得了路径选择。它没有因此赢得整条事实链。

资料来源