27、ICE候选者优先级算法:候选者对优先级计算、类型优先级与本地优先级、RFC 5245算法
ICE 候选者优先级,说白了就是给每个候选者排个座次。谁排在前面,谁就有机会先被用来建立连接。我刚开始接触 WebRTC 时,觉得这步就是个排序嘛,有啥难的?后来踩了坑才发现,这里面的门道还真不少。
为什么需要优先级?
你想想看,一个端上可能收集到几十个候选者。有 host 的、有 srflx 的、有 relay 的。如果同时拿这些候选者去连对端,网络带宽和 CPU 都扛不住。所以 ICE 协议规定:先排序,再按顺序尝试连接。
优先级高的候选者,会被优先用来做连通性检查。这就像你手里有一把钥匙,肯定先试最可能打开门的那一把。
RFC 5245 中的优先级算法
RFC 5245 里给出了一个标准公式。我每次讲到这里,都会提醒学员:这个公式不是随便写的,它背后有设计考量。
priority = (2^24) * type_preference +
(2^8) * local_preference +
(2^0) * (256 - component_id)
嗯,这里要注意:公式里的三个部分,权重完全不同。
- type_preference:类型优先级,占最高 8 位
- local_preference:本地优先级,占中间 8 位
- component_id:组件 ID,占最低 8 位
为什么 type_preference 的权重最高?因为类型决定了候选者的可靠性。host 候选者走本地网络,延迟最低;relay 候选者走中继,延迟最高。这个排序逻辑很直观。
类型优先级(Type Preference)
RFC 5245 推荐了默认的类型优先级值。我个人习惯用这套默认值,除非有特殊需求才去改它。
| 候选者类型 | 推荐优先级 | 说明 |
|---|---|---|
| host | 126 | 本地接口,延迟最低 |
| srflx | 100 | NAT 映射,需要穿透 |
| relay | 0 | 中继转发,延迟最高 |
| peer-reflexive | 110 | 连通性检查中发现的 |
我在项目中遇到过一个问题:某个客户的内网环境特别复杂,host 候选者反而连不上。当时我临时调整了 type_preference,把 srflx 的优先级调高,才解决了问题。所以默认值不是铁律,要根据实际场景灵活调整。
本地优先级(Local Preference)
本地优先级是用来区分同一类型下的不同候选者的。比如你有两个 host 候选者,一个走 WiFi,一个走以太网。你可以通过本地优先级告诉 ICE:优先用哪个。
RFC 5245 没有规定本地优先级的具体算法,留给了实现者自己决定。我一般这样处理:
- 以太网接口:高优先级(比如 65535)
- WiFi 接口:中等优先级(比如 50000)
- 移动网络接口:低优先级(比如 30000)
为什么这么分?因为以太网通常比 WiFi 稳定,WiFi 又比移动网络稳定。你想想看,视频通话时突然从 WiFi 切到 4G,那体验能好吗?
组件 ID(Component ID)
组件 ID 在公式里是 (256 - component_id)。RTP 的 component_id 是 1,RTCP 是 2。所以 RTP 的优先级天然比 RTCP 高。
这个设计很合理。RTP 承载音视频数据,RTCP 只是控制信息。如果带宽不够,肯定优先保证 RTP 的连通性。RTCP 可以等一等,甚至丢几个包也没关系。
实际计算示例
我们来看一个具体的例子。假设有一个 host 候选者,本地优先级设为 65535,组件 ID 为 1:
priority = (2^24) * 126 +
(2^8) * 65535 +
(2^0) * (256 - 1)
= 16777216 * 126 +
256 * 65535 +
255
= 2113929216 +
16776960 +
255
= 2130706431
这个值很大对吧?别担心,ICE 协议里 priority 字段是 64 位的,完全装得下。
优先级与候选者配对
优先级不仅影响单个候选者的排序,还影响候选者配对的顺序。ICE 会把本地候选者和远端候选者两两配对,然后按优先级排序。优先级高的配对,会先做连通性检查。
我曾经踩过一个坑:两个端都收集到了很多候选者,配对数量爆炸,导致连通性检查超时。后来我限制了每个类型的候选者数量,才解决了问题。嗯,这里要提醒大家:候选者不是越多越好,够用就行。
SVG 流程图:优先级计算与配对流程
避坑指南
我曾经踩过的坑:
- 候选者数量过多导致配对爆炸,连通性检查超时
- 本地优先级设置不合理,导致 WiFi 和以太网切换频繁
- 没有考虑 peer-reflexive 候选者,导致连接建立延迟
我的建议:
- 每个类型最多保留 5 个候选者,够用就行
- 本地优先级按网络接口稳定性排序,不要只看带宽
- 如果遇到连接问题,先检查候选者优先级是否合理
总结
ICE 候选者优先级算法,说白了就是一套排序规则。类型优先级决定大类,本地优先级决定小类,组件 ID 决定内部顺序。这三者组合起来,就能给每个候选者一个唯一的优先级值。
我在实际项目中,很少去改默认的类型优先级。但本地优先级经常会根据网络环境做调整。比如在移动端,我会把 WiFi 的优先级设得比 4G 高,但也会留一个降级策略,防止 WiFi 信号不好时切不过去。
嗯,优先级算法就讲到这里。记住一句话:优先级不是越高越好,而是要符合你的实际网络场景。