5. 接收初始 Offer (Receiving the Initial Offer)
当 agent 收到 initial offer 时, 它会检查 offerer 是否支持 ICE, 确定自己的角色, 收集 candidates, 对其赋予优先级, 选择 default candidates, 编码并发送 answer, 并且对于 full implementations, 形成 check lists 并开始 connectivity checks.
5.1. 验证 ICE 支持 (Verifying ICE Support)
如果在收到的 SDP 中, 每个 media stream 的每个 component 的 default destination 都出现在 candidate attribute 中, agent 将继续执行本规范定义的 ICE 过程. 例如, 对 RTP 而言, c 和 m lines 中的 IP 地址和端口分别出现在 candidate attribute 中, 且 rtcp attribute 中的值也出现在 candidate attribute 中.
如果不满足该条件, agent 必须按正常 RFC 3264 过程处理 SDP, 不使用本规范余下部分中描述的任何 ICE 机制, 但有以下例外:
-
Agent 必须遵循第 10 节的规则, 该节描述所有 agents 的 keepalive 过程.
-
如果 agent 未继续使用 ICE 是因为存在 a=candidate 属性, 但没有任何属性匹配 media stream 的 default destination, 则 agent 必须在其 answer 中包含 a=ice-mismatch 属性.
-
如果 default candidates 是通过 TURN server 学到的 relayed candidates, agent 必须在 TURN server 中为其刚从 peer 的 SDP 中学到的 IP 地址创建 permissions. 如果不这样做, peer 在 media stream 中发送的初始分组可能丢失.
5.2. 确定角色 (Determining Role)
对于每个 session, 每个 agent 都承担一个 role. 有两个 roles: controlling 和 controlled. Controlling agent 负责选择最终用于通信的 candidate pairs. 对 full agent, 这意味着 nomination 可由 ICE 为每个 media stream 使用的 candidate pairs, 并在需要时基于 ICE 的选择生成 updated offer. 对 lite implementation, 成为 controlling agent 意味着基于 offer 和 answer 中的 candidates 选择 candidate pair, 对 IPv4 而言永远只有一个 pair, 并在需要时生成反映该选择的 updated offer; 对仅 IPv4 的 host, 永远不需要. Controlled agent 被告知每个 media stream 使用哪些 candidate pairs, 且不会生成 updated offer 来发信令通知此信息. 以下各节详细描述 controlling 和 controlled nodes 实际遵循的过程.
确定 role 及其对行为影响的规则如下:
-
两个 agents 都是 full: 生成启动 ICE processing 的 offer 的 agent 必须承担 controlling role, 另一个 必须承担 controlled role. 两个 agents 都会形成 check lists, 运行 ICE state machines, 并生成 connectivity checks. Controlling agent 将执行第 8.1 节中的逻辑来 nomination 将由 ICE 选择的 pairs, 然后两个 agents 按第 8.1.2 节所述结束 ICE. 在 Appendix B.11 描述的异常情况下, 两个 agents 可能错误地都认为自己是 controlled 或 controlling. 为解决此问题, 每个 agent 必须选择一个随机数, 称为 tie-breaker, 它在 0 到 (2**64) - 1 之间均匀分布, 即一个 64-bit positive integer. 该数字用于 connectivity checks 中检测并修复此情况, 如第 7.1.2.2 节所述.
-
一个 agent 是 full, 一个是 lite: Full agent 必须承担 controlling role, lite agent 必须承担 controlled role. Full agent 会形成 check lists, 运行 ICE state machines, 并生成 connectivity checks. 该 agent 将执行第 8.1 节中的逻辑来 nomination 将由 ICE 选择的 pairs, 并使用第 8.1.2 节中的逻辑结束 ICE. Lite implementation 只监听 connectivity checks, 接收并响应它们, 然后按第 8.2 节所述结束 ICE. 对 lite implementation, 每个 media stream 的 ICE processing 状态被认为是 Running, 整体 ICE 状态也是 Running.
-
两个都是 lite: 生成启动 ICE processing 的 offer 的 agent 必须承担 controlling role, 另一个 必须承担 controlled role. 在这种情况下, 永远不会发送 connectivity checks. 相反, 一旦 offer/answer 交换完成, 每个 agent 就在没有 connectivity checks 的情况下执行第 8 节描述的处理. 两个 agents 可能都认为自己是 controlled 或 controlling. 在后一种情况下, 冲突通过承载 offer/answer 交换的信令协议中的 glare detection 能力解决. 每个 media stream 的 ICE processing 状态被认为是 Running, 整体 ICE 状态也是 Running.
一旦为 session 确定 roles, 它们会一直保持, 除非 ICE 被重启. ICE restart (第 9.1 节) 会导致重新选择 roles 和 tie-breakers.
5.3. 收集 Candidates (Gathering Candidates)
Answerer 收集 candidates 的过程与 offerer 的过程相同: full implementations 见第 4.1.1 节, lite implementations 见第 4.2 节. 推荐在收到 offer 后立即开始此过程, 先于提醒用户. 这种收集 可以在 agent 启动时开始.
5.4. 确定 Candidates 优先级 (Prioritizing Candidates)
Answerer 对 candidates 赋予优先级的过程与 offerer 遵循的过程相同: full implementations 见第 4.1.2 节, lite implementations 见第 4.2 节.
5.5. 选择 Default Candidates (Choosing Default Candidates)
Answerer 选择 default candidates 的过程与 offerer 遵循的过程相同: full implementations 见第 4.1.4 节, lite implementations 见第 4.2 节.
5.6. 编码 SDP (Encoding the SDP)
Answerer 编码 SDP 的过程与 offerer 对 full 和 lite implementations 遵循的过程相同, 如第 4.3 节所述.
5.7. 形成检查列表 (Forming the Check Lists)
只有 full implementations 形成 check lists. Lite implementations 必须跳过本节定义的步骤.
Offer/answer 交换产生的每个正在使用的 media stream 都有一个 check list. 为形成某个 media stream 的 check list, agent 会形成 candidate pairs, 计算 candidate pair priority, 按 priority 排序 pairs, 修剪它们, 并设置它们的状态. 本节描述这些步骤.
5.7.1. 形成 Candidate Pairs (Forming Candidate Pairs)
首先, agent 取出其针对某个 media stream 的每个 candidate, 称为 LOCAL CANDIDATES, 并与从 peer 收到的该 media stream 的 candidates, 称为 REMOTE CANDIDATES, 进行配对. 为防止第 18.5.2 节描述的攻击, agents 可以限制它们在 offer 或 answer 中接受的 candidates 数量. 当且仅当两个 candidates 具有相同 component ID 且具有相同 IP 地址版本时, local candidate 才与 remote candidate 配对. 某些 local candidates 可能无法与 remote candidates 配对, 某些 remote candidates 也可能无法与 local candidates 配对. 如果某个 agent 没有为 media stream 的所有 components 包含 candidates, 就会发生这种情况. 如果发生这种情况, 该 media stream 的 component 数量实际上会减少, 并被认为等于两个 agents 在该 media stream 的所有 components 中各自提供的最大 component ID 的较小值.
对 RTP 而言, 当一个 agent 提供 RTCP candidates 而另一个没有提供时, 就会发生这种情况. 另一个例子是, offerer 可以在同一端口上多路复用 RTP 和 RTCP, 并通过 SDP attribute [RFC5761] 在 SDP 中发信令表示它可以这样做. 然而, 由于 offerer 不知道 answerer 是否能够执行这种多路复用, offerer 会在不同端口上包含 RTP 和 RTCP candidates, 使 offer 对每个 media stream 有两个 components. 如果 answerer 能够执行这种多路复用, 它会为每个 candidate 只包含一个 component, 即组合的 RTP/RTCP mux. ICE 最终会表现得好像该 candidate 只有一个 component.
某个特定 component 中 local 和 remote candidates 都是 default candidates 的 candidate pairs 被称为该 component 的 default candidate pair. 顾名思义, 如果两个 agents 都不感知 ICE, 这就是将用于传输媒体的 pair.
为帮助理解, 图 6 展示了若干关键概念之间的关系: transport addresses, candidates, candidate pairs 和 check lists, 同时指示 candidates 和 candidate pairs 的主要属性.
+------------------------------------------+
| |
| +---------------------+ |
| |+----+ +----+ +----+ | +Type |
| || IP | |Port| |Tran| | +Priority |
| ||Addr| | | | | | +Foundation |
| |+----+ +----+ +----+ | +ComponentiD |
| | Transport | +RelatedAddr |
| | Addr | |
| +---------------------+ +Base |
| Candidate |
+------------------------------------------+
* *
* *************************************
* *
+-------------------------------+
.| |
| Local Remote |
| +----+ +----+ +default? |
| |Cand| |Cand| +valid? |
| +----+ +----+ +nominated?|
| +State |
| |
| |
| Candidate Pair |
+-------------------------------+
* *
* ************
* *
+------------------+
| Candidate Pair |
+------------------+
+------------------+
| Candidate Pair |
+------------------+
+------------------+
| Candidate Pair |
+------------------+
Check
List
图 6: Check List 概念图
5.7.2. 计算 Pair Priority 并排序 Pairs (Computing Pair Priority and Ordering Pairs)
Pairs 形成后, 会计算 candidate pair priority. 令 G 为 controlling agent 提供的 candidate 的 priority. 令 D 为 controlled agent 提供的 candidate 的 priority. Pair 的 priority 计算如下:
pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
其中 G>D?1:0 是一个表达式, 如果 G 大于 D, 其值为 1, 否则为 0. 一旦分配 priority, agent 就按 priority 降序排序 candidate pairs. 如果两个 pairs 具有相同 priority, 它们之间的顺序是任意的.
5.7.3. 修剪 Pairs (Pruning the Pairs)
该已排序 candidate pairs 列表用于确定将要执行的 connectivity checks 序列. 每个 check 都涉及从 local candidate 向 remote candidate 发送 request. 由于 agent 不能直接从 reflexive candidate 发送 requests, 而只能从其 base 发送, agent 接下来会遍历已排序 candidate pairs 列表. 对每个 local candidate 为 server reflexive 的 pair, server reflexive candidate 必须被其 base 替换. 完成后, agent 必须修剪该列表. 做法是: 如果某个 pair 的 local 和 remote candidates 与 priority list 中更靠前的某个 pair 的 local 和 remote candidates 相同, 则移除该 pair. 结果是一个有序 candidate pairs 序列, 称为该 media stream 的 check list.
此外, 为限制第 18.5.2 节描述的攻击, agent 必须将其在所有 check lists 上执行的 connectivity checks 总数限制为特定值, 且该值 必须可配置. 推荐默认值为 100. 此限制通过丢弃较低优先级 candidate pairs 来执行, 直到数量小于 100. 推荐在可能时使用较低值, 将其设置为实际部署配置中可能看到的合理 checks 最大数量. 要求可配置是为了在部署后发现该值有问题时, 可在现场修正该值.
5.7.4. 计算状态 (Computing States)
Check list 中的每个 candidate pair 都有 foundation 和 state. Foundation 是该 pair 中 local 和 remote candidates 的 foundations 的组合. State 在每个 media stream 的 check list 都计算完成后分配. State 可能有五个值:
-
Waiting: 尚未对该 pair 执行 check, 且一旦它成为 check list 中最高优先级的 Waiting pair 就可以执行.
-
In-Progress: 已为该 pair 发送 check, 但 transaction 正在进行.
-
Succeeded: 该 pair 的 check 已经完成并产生成功结果.
-
Failed: 该 pair 的 check 已经完成并失败, 可能从未产生任何 response, 或产生不可恢复的 failure response.
-
Frozen: 尚未对该 pair 执行 check, 且在其他某个 check 成功之前还不能执行; 其他 check 成功后, 该 pair 可解冻并进入 Waiting state.
随着 ICE 运行, pairs 会按图 7 所示在状态之间移动.
+-----------+
| |
| |
| Frozen |
| |
| |
+-----------+
|
|unfreeze
|
V
+-----------+ +-----------+
| | | |
| | perform | |
| Waiting |-------->|In-Progress|
| | | |
| | | |
+-----------+ +-----------+
/ |
// |
// |
// |
/ |
// |
failure // |success
// |
/ |
// |
// |
// |
V V
+-----------+ +-----------+
| | | |
| | | |
| Failed | | Succeeded |
| | | |
| | | |
+-----------+ +-----------+
图 7: Pair 状态 FSM
Check list 中每个 pair 的初始状态通过执行以下步骤序列计算:
-
Agent 将每个 check list 中的所有 pairs 设置为 Frozen state.
-
Agent 检查第一个 media stream 的 check list, 当该 media stream 由 SDP offer 和 answer 中第一条 m line 描述时, 它就是第一个 media stream. 对该 media stream:
- 对所有具有相同 foundation 的 pairs, agent 将 component ID 最低的 pair 的 state 设置为 Waiting. 如果存在多个这样的 pairs, 使用 priority 最高的那个.
其中一个 check list 将有若干处于 Waiting state 的 pairs, 其他 check lists 的所有 pairs 都处于 Frozen state. 至少有一个 Waiting pair 的 check list 称为 active check list, 所有 pairs 都 Frozen 的 check list 称为 frozen check list.
Check list 本身也关联一个 state, 用于捕获该 media stream 的 ICE checks 状态. 有三种状态:
-
Running: 在此状态下, 该 media stream 的 ICE checks 仍在进行.
-
Completed: 在此状态下, ICE checks 已为该 media stream 的每个 component 产生 nominated pairs. 因此, ICE 已成功, 可以发送媒体.
-
Failed: 在此状态下, 该 media stream 的 ICE checks 未能成功完成.
当 check list 作为 offer/answer 交换的结果首次构造时, 它被置于 Running state.
跨所有 media streams 的 ICE processing 也有一个关联状态. 当 ICE processing 正在进行时, 该状态等于 Running. 当 ICE processing 完成时状态为 Completed, 如果失败且未成功则为 Failed. 状态转换规则如下文所述.
5.8. 调度 Checks (Scheduling Checks)
只有 full implementations 生成 checks. Lite implementations 必须跳过本节描述的步骤.
Agent 执行 ordinary checks 和 triggered checks. 两类 checks 的生成都由为每个 media stream 周期性触发的 timer 控制. Agent 维护一个 FIFO queue, 称为 triggered check queue, 其中包含下一次可用机会应发送 checks 的 candidate pairs. 当 timer 触发时, agent 从 triggered check queue 移除顶部 pair, 对该 pair 执行 connectivity check, 并将 candidate pair 的 state 设置为 In-Progress. 如果 triggered check queue 中没有 pairs, 则发送 ordinary check.
一旦 agent 按第 5.7 节所述计算出 check lists, 它会为每个 active check list 设置 timer. Timer 每 Ta*N 秒触发一次, 其中 N 是 active check lists 的数量, 初始时只有一个 active check list. Implementations 可以将 timer 设置为更低频率触发. Implementations 应该注意分散这些 timers, 使它们不会针对每个 media stream 同时触发. Ta 和 retransmit timer RTO 按第 16 节所述计算. 乘以 N 允许将此聚合 check 吞吐量在所有 active check lists 之间分配. 第一个 timer 立即触发, 使 agent 在 offer/answer 交换完成的那一刻就执行 connectivity check, 然后在 Ta 秒后执行下一次 check, 因为只有一个 active check list.
当 timer 触发且没有 triggered check 要发送时, agent 必须按如下方式选择 ordinary check:
-
找到该 check list 中处于 Waiting state 的最高优先级 pair.
-
如果存在这样的 pair:
- 从该 pair 的 local candidate 向 remote candidate 发送 STUN check. 为此目的构造 STUN request 的过程见第 7.1.2 节.
- 将 candidate pair 的 state 设置为 In-Progress.
-
如果不存在这样的 pair:
- 找到该 check list 中处于 Frozen state 的最高优先级 pair.
- 如果存在这样的 pair:
- 解冻该 pair.
- 对该 pair 执行 check, 使其 state 转换为 In-Progress.
- 如果不存在这样的 pair:
- 终止该 check list 的 timer.
为计算 check 的 message integrity, agent 使用从 peer 的 SDP 中学到的 remote username fragment 和 password. Local username fragment 是 agent 直接知道的, 属于它自己的 candidate.