内容提要
SIP呼叫保持通过对话内re-INVITE重协商实现,现代规范以a=sendonly/inactive方向属性替代全零地址,保留真实IP以维持NAT穿透和RTCP监控。保持期间可注入单向保持音乐。双方同时发起会触发491冲突,需按所有者2.1-4秒、非所有者0-2秒随机退避。需防范NAT老化导致恢复单通,并保持编解码器一致。
延伸解读
从零地址到方向属性:SIP 呼叫保持的规范演进
早期 RFC 2543 要求用全零 IP 地址(0.0.0.0)表示媒体流停止,但这会导致 NAT 映射超时、RTCP 监控中断以及防火墙丢弃数据包。现代 RFC 3264 改用 a=sendonly、a=inactive 等方向属性,同时在 c= 行保留真实单播地址,从而维持 NAT 穿透和 RTCP 监控。这一变化反映了 SIP 在复杂网络环境下的适应性改进,也提醒开发者遵循新规范以避免互通问题。
方向属性协商:Offer/Answer 模型中的对称规则
SDP Offer/Answer 模型要求方向属性严格对称:收到 sendonly 的 Offer 必须回复 recvonly,收到 inactive 则回复 inactive。若接收端无法支持 recvonly,只能降级为 inactive,不可改为 sendrecv。这种规则确保了媒体流方向的一致性,防止协商失败。开发者需注意,错误的应答可能导致单向媒体或呼叫恢复异常。
并发保持冲突:491 状态码与随机退避机制
当双方几乎同时发起保持时,两个 re-INVITE 会碰撞,触发 491 Request Pending。根据 RFC 3261,对话内同一时刻只允许一个 INVITE 事务。为避免无限重试,协议规定所有者(初始呼叫发起方)等待 2.1 至 4.0 秒,非所有者等待 0 至 2.0 秒,非所有者优先重发以解除死锁。这一机制依赖角色区分和随机退避,实现时需正确识别所有者身份。
保持期间的网络风险:NAT 老化与编解码器漂移
若采用 a=inactive 模式,媒体流完全中断,防火墙 UDP 映射可能在 30-60 秒后超时,导致恢复时单向或双向无声。缓解方案包括持续发送 RTCP 报告、优先使用 MoH 维持端口活跃,或通过 OPTIONS 探测保持信令链路。此外,部分客户端在重协商时可能重新选择编解码器,造成载荷类型或优先级不一致,引发解码失败。因此,整个会话生命周期内必须保持编解码器参数严格一致。
Q&A
SIP呼叫保持是如何实现的?
SIP呼叫保持通过在已建立的对话内发送re-INVITE请求,重新协商SDP媒体属性来实现。现代规范使用a=sendonly或a=inactive方向属性,并保留真实IP地址以维持NAT穿透和RTCP监控。
为什么SIP呼叫保持不再使用全零IP地址?
全零IP地址(0.0.0.0)会导致NAT映射失效、RTCP监控中断,且可能被防火墙拦截。RFC 3264废弃了该机制,改为使用方向属性(如a=sendonly)并保留真实单播地址,以维持NAT穿透和RTCP监控。
SIP呼叫保持时双方同时发起re-INVITE会发生什么?
双方同时发起re-INVITE会导致冲突,每一方都会收到491 Request Pending响应。随后,呼叫发起方(所有者)在2.1至4.0秒内随机退避,应答方(非所有者)在0至2.0秒内随机退避,非所有者先超时重发,从而解除死锁。
如何防止SIP呼叫保持期间NAT老化导致恢复单通?
可以采取以下措施:1. 保持期间周期性发送RTCP Receiver Report包刷新NAT映射;2. 优先采用音乐保持(MoH)方案,持续发送单向媒体包;3. 边缘代理或SBC发送OPTIONS探测包维持信令链路。
SIP呼叫保持时SDP的o=字段中sess-version有什么作用?
sess-version是会话描述的版本号,每次修改媒体属性、端口或连接地址时必须单调递增。若未变化,协议栈会认为媒体参数无改动而忽略重协商请求。
在PJSIP中如何实现呼叫保持和恢复?
使用pjsua_call_set_hold函数发起保持,它会将SDP属性设置为a=sendonly或a=inactive并递增sess-version;使用pjsua_call_reinvite函数恢复正常通话,触发重协商恢复a=sendrecv。