摘要

  • PRACK 确认的是一份可靠的 101—199 临时响应,而不是对原始 INVITE 的最终接受;对 PRACK 返回 2xx,也不能被读成通话已接通。
  • RSeq 标识临时响应,RAck 把回执绑定到该响应的 RSeq、原请求的 CSeq 编号与方法;这套有序状态还能承载 offer/answer,但不能证明媒体已经抵达或被听见。

先把“进展”从“结果”里拆出来

RFC 3261 允许处理 INVITE 的用户代理在最终响应之前发送一个或多个 1xx。101 至 199 可以表示正在振铃、呼叫正在前进,也可以建立 early dialog;2xx 才表示接受,3xx 至 6xx 则给出其他最终处置。基础规范同时指出,这些进展响应并不可靠传送。

这不是措辞上的小区别。INVITE 可能经过代理,可能分叉到多个终端,也可能在传统电话网络互通时持续很久。一次临时响应丢失,不一定会改变最后能否接通,却可能丢掉建立早期对话、协商会话描述或解释呼叫进展所需要的状态。

RFC 3262 为此引入 100rel 能力与 PRACK 方法。若 INVITE 在 Supported 中声明 100rel,服务器可以可靠发送非 100 的临时响应;若 INVITE 在 Require 中要求它,服务器就必须可靠发送,否则应拒绝这一扩展要求。100 Trying 被排除,因为它是逐跳响应,而 RFC 3262 的临时响应可靠性是端到端机制。

一份临时响应怎样得到自己的编号

可靠临时响应同时携带 Require: 100rel 与 RSeq。第一份响应的 RSeq 从规定范围内选择,编号空间属于一笔事务:在另一笔事务里看到同一个数字,并不能证明是同一份消息。

服务器从 T1 开始按指数退避重传这份响应,直到收到匹配的 PRACK。匹配不只靠 Call-ID 或一个裸序号。PRACK 的 RAck 带三个坐标:临时响应的 RSeq、该响应所引用的 CSeq 编号,以及 CSeq 的方法名;它还必须处在同一对话中。这样,回执回答的是“这一笔 INVITE 里的这一份临时响应”,不是“这通电话大概有过进展”。

若 PRACK 匹配未确认的可靠临时响应,服务器对 PRACK 返回 2xx,并停止相应重传;若没有任何待确认响应与之匹配,则返回 481。这里存在两笔不同的判断:2xx 对应 PRACK 事务处理成功,原始 INVITE 是否接受仍由它自己的最终响应决定。

PRACK 因而只是“作用类似 ACK”,并不是 ACK 的别名。它是一个普通的对话内非 INVITE 请求,像 BYE 一样具有自己的逐跳事务可靠性和自己的响应。把两者混在一个告警或状态位里,会直接擦掉 RFC 3262 费力建立的边界。

为什么回执不能累计

PRACK 回执不是累计确认。RFC 3262 为拥塞控制建议一次只让一份可靠临时响应处于未确认状态,并强制第一份得到确认后才发送第二份。后续 RSeq 必须恰好递增一,不能循环回绕。

接收端若看见同一 dialog ID、CSeq 与 RSeq 的重传,应丢弃重复内容,而不是重新执行。若后一份响应越过了缺失的序号,它既不能被 PRACK 确认,也不能继续处理;实现可以暂存它,等待缺口补齐。这不是为临时响应建立一条无限消息流,而是在一次初始请求内部保留受限、可核对的顺序。

若持续 64*T1 仍收不到对应 PRACK,规范说服务器应以 5xx 拒绝原请求。这说明可靠性也会创造新的失败面:定时器、未确认列表、对话路由和序号状态都必须一致。标准给出了处置规则,却不证明每个设备都实施正确,也不提供现实网络中的故障比例。

媒体协商可以先走一步,最终决定仍然在后面

PRACK 可以带消息体,这让它与 offer/answer 发生关键交叉。若 INVITE 已携带 offer,可靠临时响应可以给出 answer;若 INVITE 没有 offer,第一份可靠消息可以在临时响应里提出 offer,PRACK 就必须携带 answer。若 PRACK 自己提出 offer,对 PRACK 的 2xx 则携带 answer。

这时,会话参数可能在 INVITE 得到最终响应前已经建立。RFC 3262 甚至要求:如果一份尚未 PRACK 确认的可靠临时响应携带会话描述,服务器在接受 INVITE 时必须延后最终 2xx,直到该临时响应得到确认。这里保护的是 offer/answer 交换的可靠性,而不是让 PRACK 替最终接受表态。

RFC 6337 后来进一步澄清:即使双方都支持 100rel,也不意味着每份临时响应都会可靠发送;在带 offer 的 INVITE 场景中,可靠非失败响应里的第一份 SDP 才具有真实 answer 的地位,之前不可靠响应里的 SDP 只能视作预览。

RFC 3311 定义的 UPDATE 又允许在 early 或 confirmed dialog 中修改会话参数。但 UPDATE、PRACK 与 INVITE 并没有因此合并成一件事。每个方法仍有自己的 CSeq、事务响应和状态约束。哪个交换完成、哪个仍在等待,必须分别记录。

听见声音不是收到信令的证明

RFC 3960 讨论了 early media:回铃音、排队录音、资费提示或 DTMF 交互可能在最终响应之前出现,媒体与 SIP 信令路径又是松耦合的。媒体甚至可能先于描述它的信号被观察到。

所以,PRACK 不能证明 RTP 包已经穿越网络,不能证明某人听见了提示,也不能证明通话已被对方接受。反过来,听见一段早期语音也不能替代 RAck 对特定可靠临时响应的确认。这两个观察可以相关,却不是彼此的授权凭据。

注册表能证明什么

IANA SIP 参数注册表 列出 PRACK 方法、RAck 与 RSeq 头字段,以及 100rel option tag,并把它们指向 RFC 3262。它证明这些名称和语法拥有正式分配,不证明运营商已经部署、设备正确互通或网络中的某次 1xx 真被可靠处理。

安全边界同样不能省略。RFC 3262 警告,攻击者注入 PRACK 可能使重要临时响应停止重传,因此 PRACK 应像其他请求一样认证。序号匹配可以证明状态坐标吻合,却不能单独证明发送者身份。

这套机制最持久的价值,不是把“振铃”变得更像“接通”,而是拒绝这种偷换。它允许系统明确记录:临时状态已收到,媒体协商可能推进,原始邀请仍未最终决定。

来源