候选 · host / srflx / prflx / relay
ICE 的第一步是收集候选——每一个「我可能被联系到的地址」。这一章读 api/candidate.h 的 IceCandidateType,讲清四种候选:主机候选、服务器反射候选、对端反射候选、中继候选,它们各自怎么来、优先级如何,以及为什么要同时准备这么多条备选。
候选 · host / srflx / prflx / relay
ICE 四步的第一步是「收集候选」。候选(candidate) 就是一个「我可能被对端联系到的传输地址」——一个 IP、端口、协议的组合。ICE 的哲学是广撒网,所以每一端都会尽可能多地收集候选。这一章讲清候选的四种类型,它们是理解 ICE 的基础词汇。
四种类型
api/candidate.h 里的 IceCandidateType 枚举,干净利落地给出四种:
// api/candidate.h
enum class IceCandidateType : int { kHost, kSrflx, kPrflx, kRelay };它们对应四种「怎么得到这个地址」的方式,从「最直接」到「最兜底」:
四种候选,四种「找到我」的路径:
kHost(主机候选) —— 设备本机网卡的地址(如192.168.1.5:50000)。最直接:如果两端在同一局域网,用它就能直连,零绕路。但跨 NAT 时它对外没用。kSrflx(服务器反射候选,server-reflexive) —— 设备经 STUN 服务器问出的公网地址。设备向 STUN 服务器发个包,服务器回答「我看到你来自203.0.113.7:60000」——这个「NAT 外面看到的地址」就是 srflx 候选。这是穿越 NAT 的主力。kPrflx(对端反射候选,peer-reflexive) —— 在连通性检查过程中意外发现的地址。有时对端探测包来自一个双方都没预料到的地址(某些 NAT 行为),这个新地址就地成为一个 prflx 候选。它是「检查中冒出来的惊喜」。kRelay(中继候选,relay) —— TURN 中继服务器分配的地址。当直连怎么都不通(对称 NAT、严格防火墙),就让媒体经 TURN 服务器转发。它最兜底、也最费(绕一道服务器、增延迟、耗服务器带宽),但保证能通——只要两端都连得上 TURN。
本地候选 vs 远端候选
候选分两个来源(g3doc/ice.md):
- 本地候选:自己的
Port采集出来的,通过信令发给对端。 - 远端候选:对端发来的,经信令收到后,用
P2PTransportChannel::AddRemoteCandidate加进来。
注意:候选的交换不必等 SDP 全部完成。现代 WebRTC 用 Trickle ICE——候选一边采集、一边陆续通过信令发给对端(而非等全部采完再一起发),这样连通性检查能更早开始,建连更快。你在 PeerConnectionObserver 里反复收到的 OnIceCandidate 回调,就是候选在「trickle」。
优先级:先试快的
一端往往有好几个候选,先试哪个?ICE 给每个候选算一个优先级,配成 Connection 后按优先级高低排序检查。大致的偏好顺序:
host(最快,同网直连) > srflx(穿 NAT 直连) > relay(中继,最慢但保底)Candidate(api/candidate.h)结构里带着 type()、地址、协议(UDP/TCP)、priority()、以及一个 foundation(用于把「同类同源」的候选归组)。优先级的计算综合了类型、网络接口、协议等因素——目标是让延迟低、代价小的路先被选中,中继只在别无选择时才上。
为什么要准备这么多
初学者常问:既然 host 通不了就用 srflx、srflx 不行再用 relay,为什么不按顺序试,非要一次全备齐?
因为你事先不知道哪条能通——NAT 的行为千奇百怪。 有的 NAT 让 srflx 直连轻松成功,有的(对称 NAT)会让 srflx 也失败、只能走 relay;两端 NAT 类型的组合更是难以预测。ICE 的智慧就在于不预测、全备齐、并行试:同时收集 host / srflx / relay,同时对所有配对做连通性检查,让「哪条能通、哪条最快」在实测中自然分出胜负。这比「猜一条、失败再换」快得多、也可靠得多。代价是要维护一堆候选和连接,但换来的是高穿透率和低建连延迟——非常值。
小结与下一章
候选是「我可能被联系到的地址」,四种(IceCandidateType):kHost(本机网卡,同网直连)、kSrflx(STUN 问出的公网地址,穿 NAT 主力)、kPrflx(检查中意外发现)、kRelay(TURN 中继,最兜底保底)。候选分本地 / 远端,用 Trickle ICE 边采边发(OnIceCandidate 回调)。每个候选有优先级(host > srflx > relay),让快的先试。ICE 全备齐、并行试,不靠预测靠实测。
候选从哪来?靠 Port 去采集,而 Port 由 PortAllocator 创建。下一章看这套采集机制,以及四种 Port 分别产哪种候选。👉 PortAllocator 与候选采集。
参考资料
- [1] libwebrtc 源码.
api/candidate.h(IceCandidateType、Candidate结构)。 - [2] libwebrtc 源码.
g3doc/ice.md(候选类型与本地 / 远端来源)。 - [3] IETF RFC 8445「ICE」§ Candidates;RFC 8838「Trickle ICE」。