STUN 协议 · ICE 的万能工具
STUN 是 ICE 里出场最多的协议,身兼两职:问出自己的公网地址(srflx 候选),以及给候选对做连通性检查(STUN ping)。这一章读 p2p 的 stun.cc,讲清 STUN 消息结构、Binding 请求/响应、XOR-MAPPED-ADDRESS 怎么让你「照镜子」看到公网地址。
STUN 协议 · ICE 的万能工具
在 ICE 里,有一个协议出场最频繁、身兼数职——STUN。采集 srflx 候选靠它,连通性检查也靠它,连保活都靠它。搞懂 STUN,ICE 就通了一大半。这一章读 p2p 里的 STUN 实现。
STUN 是什么:一面「照公网的镜子」
STUN(Session Traversal Utilities for NAT) 最基础的用途,是回答一个问题:「我的公网地址是什么?」 机制简单得漂亮:
你向 STUN 服务器发一个包,服务器如实告诉你「我看到这个包来自哪个地址」——那就是你在 NAT 外面的样子。 你的包出 NAT 时,源地址被 NAT 改成了公网地址 + 某个端口;STUN 服务器收到后,把这个「它看到的源地址」原样回给你。于是你就照到了自己的公网镜像——这正是 srflx 候选。
这就是 StunPort 采集 srflx 候选的原理:向配置的 STUN 服务器(RTCConfiguration 里的 iceServers)发一个 STUN 请求,拿回响应里的公网地址,当作一个候选发给对端。STUN 服务器极轻量(只是「照镜子」,不转发媒体),所以公共 STUN 服务器遍地都是、几乎免费——这和要转发所有媒体、成本高昂的 TURN 形成鲜明对比。
STUN 消息:Binding 请求与响应
STUN 消息的核心类型,p2p 的 stun.h 里定义得清清楚楚:
// p2p 的 stun.h(节选)
STUN_BINDING_REQUEST = 0x0001, // Binding 请求(「告诉我你看到的我」)
STUN_BINDING_INDICATION = 0x0011, // Binding 指示(单向,常用于保活)
STUN_BINDING_RESPONSE = 0x0101, // Binding 响应(「我看到你来自 X」)一条 STUN 消息的结构:消息类型(上面这些)+ 事务 ID(transaction id)(把响应和请求对上号)+ 一串属性(attributes)。最关键的属性是响应里的 XOR-MAPPED-ADDRESS——它装着「服务器看到的你的地址」(做了 XOR 混淆,避开某些 NAT 会篡改裸地址的毛病)。stun.cc 里 StunMessage 类负责这些消息的解析与构造(读写字节、校验、加属性)。
STUN 的第二职:连通性检查
STUN 在 ICE 里更繁忙的角色,是连通性检查——即给候选对做「ping」。Connection 对象通过互发 Binding 请求 / 响应,来验证「这条候选对,包能不能一来一回地通」:
- 本端
Connection向对端候选发一个STUN_BINDING_REQUEST。 - 对端收到,回一个
STUN_BINDING_RESPONSE。 - 收到响应 = 这条路双向可达——这个候选对「通了」。
这些检查用的 Binding 请求,和查公网地址的那种,格式一样但带上了 ICE 凭据:请求里的 USERNAME 属性带着协商好的 ice-ufrag,还有 MESSAGE-INTEGRITY 用 ice-pwd 做的完整性校验。这样对端能确认「这个探测确实来自我要通话的那一方」,而非攻击者。同一个 STUN Binding,查地址时是『照镜子』,检查时是『敲门确认』。
保活与保持 NAT 绑定
选定一条连接后,STUN 还有第三职:保活。g3doc/ice.md 说明,Connection 会持续发 STUN 探测,目的有二——维持 NAT 绑定(NAT 的端口映射会超时,不发包就被回收,路就断了)和确认对端还想继续(对方若走了,探测会失败)。所以哪怕通话中一切正常,STUN ping 也在底下默默地、周期性地跳动,像心跳一样维系着这条来之不易的通路。
小结与下一章
STUN 是 ICE 的万能工具,身兼三职:① 采集 srflx——向 STUN 服务器发 Binding 请求,响应里的 XOR-MAPPED-ADDRESS 就是你的公网镜像(服务器极轻量、近乎免费);② 连通性检查——Connection 互发 Binding 请求 / 响应验证候选对可达,请求带 ICE 凭据防冒充;③ 保活——周期性 ping 维持 NAT 绑定。消息类型 BINDING_REQUEST(0x0001)/ RESPONSE(0x0101),stun.cc 的 StunMessage 负责解析构造。
STUN 能穿越大多数 NAT,但对称 NAT 会让它失效——这时只能上 TURN 中继。下一章看这个「保底方案」。👉 TURN 中继。
参考资料
- [1] libwebrtc 源码.
p2p的stun.h/stun.cc(StunMessage、Binding 类型、XOR-MAPPED-ADDRESS)。 - [2] libwebrtc 源码.
p2p/base/stun_port.h(StunPort采集 srflx)、g3doc/ice.md。 - [3] IETF RFC 8489「Session Traversal Utilities for NAT (STUN)」。