摘要
- RFC 3261 用 Call-ID、本地 tag 和远端 tag 标识一段 SIP dialog;同一个 tag 到达对端后会从“本地”变成“远端”。
- 一次分叉的 INVITE 可以建立多段 early 或 confirmed dialog。发起方的 Call-ID 和 tag 不变,各响应方用不同 To-tag 补全各自的对话标识。
- dialog 保存序号、路由集和远端目标,使后续请求能在新事务中延续上下文;它不是人的身份、媒体流标识或通话成功证明。
一个邀请为什么会长出多个后续
“呼叫”听起来是一件单数事件:输入一个地址,等待一个人应答,然后开始交谈。互联网电话的寻址却允许另一种现实。代理可把同一个 INVITE 同时送往桌面电话、软终端和网关。多个设备可能先后响铃,甚至不止一个返回成功响应。
最初的消息事务无法独自承担随后的一切。事务只需把请求、重传和响应关联起来;一旦对端应答,ACK、重新协商、订阅或 BYE 都会成为新的事务。任何一方都可以发起下一次请求,沿途代理可能要求继续留在路径中,终端也可能在关系存续期间改变可达地址。SIP 因而需要标识一份比初始 INVITE 更长寿的解释上下文。
1999 年的 RFC 2543 把它称为 call leg,用 Call-ID、To 和 From 共同识别。该版本已经发现分叉问题:当请求可能到达多个 UAS 时,响应方应加入 To-tag,让发起方区分来自不同实例的响应;一次 INVITE 得到的多个带不同 tag 的响应,分别代表不同 call leg。
但这个模型仍借用了地址字段的整体含义,From-tag 也不是处处强制。Route 与 Record-Route 的构造尚不充分。2002 年的 SIP 2.0 没有否定分叉事实,而是把状态层次定义得更精确。
完整名称必须由两端共同完成
RFC 3261 用 dialog 取代旧 call leg。dialog 是两个用户代理之间持续一段时间的点对点 SIP 关系;它为消息提供解释环境,安排双向序列,并保留后续请求所需的路由信息。
在每个用户代理看来,dialog ID 都由三个部分组成:Call-ID、本地 tag 和远端 tag。这里的“本地”和“远端”不是全球固定名称。Alice 的本地 tag,到了 Bob 那边就是远端 tag;Bob 的本地 tag,在 Alice 那边则是远端 tag。双方同意的是两段不透明值,而不是一个统一视角。
初始请求携带 Call-ID 和 From-tag,相当于先写下半个 dialog ID。能够建立 dialog 的响应再加入 To-tag。对发起方而言,From-tag 成为本地 tag,To-tag 成为远端 tag;对响应方而言方向反转。
这正是分叉需要的控制分配。所有分支都继承同一个 Call-ID 和发起方 tag,各终端却独立生成自己的 To-tag。于是多个响应可以共享“这组信令从何而来”,同时不被误合并成同一段点对点状态。
Call-ID 故意不是完整 dialog 标识。tag 也不是用户名或凭据。RFC 3261 要求生成的 tag 具有全局唯一性和至少 32 位密码学随机性,目的是减少标识冲突;这不能证明 Alice 的真实身份,不能授权 Bob,也不能担保显示名称与真人相符。
最终接听之前,对话已经可以存在
对 INVITE 而言,带 To-tag 的 101 至 199 临时响应会建立 early dialog。发起方因而能在最终结果尚未确定时,就分别保存各响应方的状态。对应的 2xx 把它推进为 confirmed dialog;失败响应或该路径最终没有成功,则结束早期状态。
在分叉场景中,发起方可以同时拥有多段 early dialog:一台设备响铃,一台报告进度,另一台拒绝。它们共享发起方贡献,却拥有不同远端 tag。甚至可能有多个 2xx 先后到达,应用必须分别确认相关 dialog,再决定保留哪一段关系并终止多余结果。
这与 PRACK 相邻,却不是同一个问题。PRACK 及 RSeq/RAck 解决重要临时响应是否可靠、有序到达;tag 解决响应属于哪一个 peer context。可靠性机制在已命名的 early dialog 中运行,并不代替 dialog 的命名。
Via branch 也处在另一层。branch 帮助匹配某一次事务及其重传;Call-ID 与双方 tag 在该事务结束后继续存在,并出现在新的事务里。把两者混为一谈,要么会在事务结束时过早删除持续状态,要么会把多个请求错误合成一次事务。
名称背后是一组可执行状态
RFC 3261 保存的不只是三段 header 值。dialog 状态还包括本地与远端序号、本地与远端 URI、remote target、安全标志和有序 route set。
两个方向各有自己的 CSeq 进度。用户代理生成新的 in-dialog 请求时推进本地序号;接收方保存远端序号,可拒绝低于既有值的乱序请求。序号出现缺口不一定错误,因为某次尝试可能在代理认证挑战处停止,根本没有到达最终 UAS。序列提供的是方向性顺序证据,不是每个整数都必须出现的账本。
route set 说明哪些代理要求继续位于后续信令路径。UAS 按请求中 Record-Route 的顺序保存,UAC 从响应中反向保存。RFC 3665 的完整呼叫流把这套规则画成消息:同一个 Call-ID 和双方 tag 贯穿 ACK、BYE 与响应,Route header 则把被选中的代理留在路径里。
remote target 回答另一个问题:当前应把请求送往对端的哪个 Contact。它在建立 dialog 时取自 Contact,并可由成功的 target-refresh request 更新。对于 INVITE 建立的 dialog,RFC 3261 的核心更新方法是 re-INVITE;其他扩展可为不同 dialog 类型定义方法。
目标改变不等于身份改变。设备可以公布新 Contact,dialog 的 Call-ID 与双方 tag 仍保持,route set 也不会因此重写。换句话说,dialog tuple 指向“哪份持续上下文”,route set 决定“经过哪些保留节点”,remote target 决定“到达当前哪个终端地址”。三个问题不能塞进同一个字段。
信令上下文并不是音频本身
dialog 建立后,任何一端都可以在其中发起新事务;这次发送者成为该事务的 UAC,接收者成为 UAS,不受初始 INVITE 中角色限制。持续关系因此跨越了一次客户机—服务器交换。
媒体协商由另一层承担。RFC 3264 用 SDP offer/answer 协商媒体流、编解码、地址和端口,并假定 SIP 这样的上层协议提供关联上下文。同一 dialog 中的后续 offer 可以增加、删除或修改媒体流。
所以 confirmed dialog 不能证明音频已经送达,更不能标识某个 RTP source 或证明通话质量。它只说明双方拥有可解释后续信令的上下文;媒体平面的成败仍需媒体证据。
RFC 4028 又为会话增加协商定时器,以 re-INVITE 或 UPDATE 定期刷新。它明确允许同一个 INVITE 产生的不同 dialog 使用不同间隔,甚至有的没有定时器。刷新是持续 dialog 里的新事务;成功延长会话,未能按时刷新可能触发 BYE。三段标识并不是永久租约。
一段 dialog 还可能容纳多个 usage
扩展进一步暴露了“一个 dialog 就是一通电话”的局限。INVITE 建立 invite usage,而 dialog 内的 SUBSCRIBE 或 REFER 还可能建立额外 usage。RFC 5057 说明这些 usage 共享 Call-ID、tag、CSeq、route set、Contact、remote target 与安全标志,却各自保存订阅期限等专属状态。
结束一个 usage 不一定结束另一个。BYE 可以终止 invite usage,而事件订阅继续存在;订阅终止也不必摧毁邀请关系。共享 dialog 是信令容器,不是所有应用关系必须同生共死的承诺。
RFC 6665 在事件通知中给出具体例子:SUBSCRIBE 与 NOTIFY 使用 dialog 状态,但还要用 Event 字段匹配具体订阅;一次分叉 SUBSCRIBE 可能建立多段各自刷新的 dialog。Call-ID/tag tuple 找到共享上下文,却不是每一种上层用途的完整主键。
SIP 的三段名称之所以重要,不是因为它声称识别一切,而是因为它只识别恰当的一层。当事务结束、目标移动、路由保留、媒体改变、应用状态叠加时,点对点上下文仍有一个双方共同完成的名字。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
