摘要

  • RFC 3605 增加了媒体级 a=rtcp 属性,用来显式写明 RTCP 端口及可选地址,因为 NAT 可能把原本成对的 RTP、RTCP 映射成不相邻端口,甚至不同公网地址。
  • 这行文本是目的地声明,不是运行结果。解析、协商、套接字绑定、映射观察、发包、到包、RTCP 验证、报告入库和质量决策必须分别留证。

故障单上有三个看似一致的事实:SDP 校验通过,音频包计数持续增加,页面上也显示了一个 RTCP 端口。第四个事实却没有出现——接收报告计数始终为零。运营平台把前三个事实合并成“控制通道正常”,于是最关键的缺口从告警中消失了。

RFC 3605 在 2003 年 10 月以 Standards Track 发布,它处理的正是一个很小、很具体的协调问题。早期 RTP/RTCP 部署习惯使用一对 UDP 端口:RTP 使用媒体端口,RTCP 使用紧随其后的奇数端口。SDP 只写一个媒体端口,接收方通过算法推导控制端口。

端口映射 NAT 破坏了这个前提。内部相邻的两个端口,到了公网侧不一定继续相邻,奇偶关系也可能颠倒。如果 NAT 管理一组公网地址,两个映射甚至可能落到不同地址。继续用“媒体端口加一”推断控制端口,不再是简化,而是无证据的猜测。

RFC 给出的修补方式是媒体级属性:a=rtcp: 后面写端口,还可以附带网络类型、地址类型和连接地址。它不得被当作会话级属性使用。这个设计把无法可靠推导的坐标变成了可以显式交换的坐标。

但“能够表达”与“已经发生”之间仍隔着整条运行链。生成器写出属性,只证明生成器的输出。解析器接受属性,只证明语法和局部语义通过。Offer/Answer 达成,只证明双方记录了一个协商结果。它们都不能证明接收进程绑定了端口,也不能证明防火墙和 NAT 允许报文到达。

RFC 3605 还主动保留了一种部分失败模式。作者没有扩展媒体行,而是选择新增属性,因为旧实现遇到不认识的属性可以忽略它,而不必拒绝整个媒体描述。结果是:旧实现可能继续接收 RTP 媒体,却不会把 RTCP 发往显式写明的端口。音频可用与控制反馈失效可以同时为真。

这正是单一“会话成功”状态的危险之处。用户能听见声音,只能说明某个方向、某个时间段内的媒体路径有结果。它不说明对端理解了 a=rtcp,不说明控制数据包走到了声明地址,也不说明接收报告被监控系统读取。短时间的好音质,更不能替代长期丢包、抖动和同步反馈。

映射发现也有严格的证据范围。RFC 描述了一种 STUN 流程:主机分配 RTP 和 RTCP 两个 UDP 端口,分别向 STUN 服务器发包,由服务器返回看到的外部地址和端口。随后主机可以把这些坐标写入 SDP。

同一段文字立即指出其假设:NAT 对 STUN 服务器和最终 SDP 对端必须使用同样的转换,而现实部署中的所有 NAT 并不保证这一点。因此 STUN 响应是一名观察者在一个时刻看到的事实,不是一张面向所有目的地、永久有效的通行证。

如果审计记录只留下“发现成功”,就丢掉了限定事实成立的坐标。至少应保留内部 tuple、STUN 观察者、外部 tuple、传输协议、时间、接口、预期对端、会话版本,以及观察与首次使用之间的间隔。映射过期、接口切换或目的地相关映射都可能让原本真实的观察失去适用性。

发送端计数也不能跨越接收边界。应用把字节交给 socket,是一次本地动作;主机出口抓到包,是另一个观察点;远端边界抓到包,才证明它到达那个边界;RTCP 解析器接受并关联到正确会话,又是下一层。每个收据只能回答自己的问题。

即使接收报告真的到达,也要限制它的含义。RFC 3550 中,RTCP 承担接收质量反馈、参与者识别、同步等控制功能。报告关联特定报告源、同步源和统计区间。它不是远端真人身份凭证,不证明所有方向都被观察,也不直接证明用户体验或业务目标达成。

相反,报告缺失的原因很多。对端可能忽略未知属性,可能协商了单端口复用,可能仍向相邻端口发送,可能遇到过期映射或过滤规则,也可能尚未到报告调度时点。数据包可能已经抵达但采集链断裂,指标也可能存在却没有暴露。零条记录不是一份“零丢包”报告。

后续规范增加了选择,却没有取消证据分层。RFC 5761 允许 RTP 与 RTCP 在协商后复用同一端口。RFC 8859 在 SDP 复用分析中把 rtcp 归为 TRANSPORT 属性。IANA 当前的 SDP 参数登记仍列出这一媒体级属性。运营必须保存实际协商的模式,不能用某个产品默认值回填历史事实。

RFC 5389 对 STUN 的定位同样重要:它是一件工具,不是完整 NAT 穿越方案。最小机制负责最小承诺。发现负责提供观察,SDP 负责传递声明,运行代码负责打开端口并发包,网络负责实际传输,监控负责验证和保存。任何一层都不应冒领下一层的完成状态。

信令完整性也只能解决声明是否被篡改。RFC 3605 提醒,能够改写 SDP 的攻击者可以把 RTCP 流量重定向到别处,并讨论端到端完整性保护。签名或完整性校验通过,能增强“谁声明了什么”的可信度;它无法让关闭的端口自动开放,也不证明目的地获准接收遥测。

因此状态机应该展开,而不是压缩。保存原始 SDP 及哈希;保存解析器和协商结论;保存分离端口或复用模式;保存套接字和进程;保存映射观察者、时刻和 tuple;保存声明坐标及完整性结果;再分别记录首次发包、远端首次到包、首次有效 RTCP、首次报告、报告覆盖区间、指标入库和由此触发的动作。

这个链条的价值不只是排障。它阻止自动化把“没有看见”误当成“测量值为零”。如果质量算法把缺失报告当作无丢包,最安静的故障反而会获得最高分;如果把所有缺失都当成网络中断,又会制造大量错误修复。缺失首先是一种证据状态。

RFC 3605 做到了它该做的事:让 NAT 后不可推导的 RTCP 目的地可以被明确说出。它没有替运行代码交付数据包,也没有替运营者解释沉默。标准给出坐标,真正的网络必须提供从坐标到报告的可验证路径。