SIP 是一个基于文本的应用层信令协议,看上去和 HTTP 很像,但它面对的网络环境比传统 Web 请求更复杂:终端可能在 UDP 上通信,链路可能丢包,响应可能延迟到达,代理服务器可能发生分叉转发。因此,SIP 不能只依赖“发一次、等结果”,而必须设计一套完整的重传机制与定时器(Timer)体系来保证请求、响应与事务状态能够被正确推进。
很多人在学 SIP 时,只记住了几个结论:
- UDP 下会重传,TCP 下通常不需要
- INVITE 和非 INVITE 的行为不一样
- T1、T2、T4 很重要,但总是记不清
这篇文章不只停留在“背规则”,而是从为什么需要重传、SIP 事务层如何工作、各类 Timer 的职责、实际超时现象如何表现几个角度,把这套机制讲清楚。
一、为什么 SIP 需要重传机制
如果 SIP 全部跑在 TCP 上,那么很多可靠性问题可以交给传输层处理。但现实并不是这样。SIP 在大量部署里长期依赖 UDP,原因包括:
- 实现简单,开销小
- 适合短报文交互
- 早期设备与网络环境广泛支持
- 和实时通信场景匹配较好
但 UDP 的问题也很明显:
- 不保证报文送达
- 不保证顺序
- 不保证不重复
- 不提供连接状态
于是 SIP 自己必须解决两个问题:
- 请求发出去后,如果对方没收到怎么办?
- 响应回来了,但原始发送方没收到怎么办?
这就是 SIP 事务层存在的核心原因。它不是简单记录“发过什么消息”,而是通过一套状态机 + 定时器 + 匹配规则来管理请求和响应的生命周期。
二、先分清三个概念:Transaction、Dialog、Session
很多人把 SIP 重传学乱,是因为一开始就把事务层、对话层、会话层混在一起了。先把这三个概念分开,后面 Timer 才不容易记错。
1. Transaction(事务)
事务是最底层、最直接和“请求-响应”对应的单位。通常可以理解为:
- 一个请求
- 以及和它相关的一组响应
例如一个 INVITE 请求以及对应的 100 Trying、180 Ringing、200 OK,构成一个 INVITE 事务。
2. Dialog(对话)
对话用于描述两个 UA 之间的逻辑关系,由 Call-ID、From tag、To tag 等字段标识。一个对话里可以包含多个事务,例如:
- INVITE 事务
- re-INVITE 事务
- BYE 事务
- UPDATE 事务
3. Session(媒体会话)
会话更偏向媒体层和 SDP 协商层面,强调的是音视频流、编码、端口、媒体方向等内容。
关键点: SIP 的重传机制,主要发生在事务层,不是对话层,也不是媒体会话层。
三、SIP 重传机制到底在解决什么
SIP 重传并不是“只要超时就一直重发”,它要处理的本质是:
- 请求丢失
- 响应丢失
- 网络延迟过大
- 重复消息到达
- 代理分叉导致的多路响应
因此 SIP 的设计目标并不是绝对可靠,而是:
- 在不可靠传输层上尽可能可靠地推进状态
- 避免无限重传
- 避免旧消息破坏当前状态
- 在等待无果时及时释放资源
四、T1、T2、T4 是什么
在 SIP 中,很多 Timer 的计算都建立在三个基础时间参数上:
1. T1
T1 是一个基础 RTT(Round-Trip Time)估计值,默认通常为 500ms。它不是精确测量值,而是 SIP 协议栈用于估算重传起点的默认时间。
很多重传间隔都以 T1 为起点,然后指数退避。
2. T2
T2 是非 INVITE 请求重传的上限值,默认通常为 4s。它的作用是防止退避间隔无限增长太快,也避免网络里形成过于稀疏但长期占资源的等待。
3. T4
T4 表示消息在网络中仍可能存在的最长持续时间,默认通常为 5s。它可以理解为“网络残留报文寿命”的估计值,用于帮助事务状态机判断:什么时候可以安全地进入终结状态,不必再担心旧报文突然回来。
五、INVITE 事务为什么特殊
在 SIP 里,INVITE 与非 INVITE 请求被区别对待。原因在于 INVITE 是建立会话的核心请求,它可能经历:
- 长时间振铃
- 临时响应链路较长
- 最终响应到达时间不固定
- 需要 ACK 对 2xx 进行单独处理
这意味着 INVITE 事务比普通 REGISTER、OPTIONS、BYE、MESSAGE 等请求复杂得多。
1. INVITE 客户端事务的重传
在 UDP 下,UAC 发送 INVITE 后,如果没收到响应,会按照指数退避进行重传:
- 第一次等待 T1
- 第二次等待 2*T1
- 第三次等待 4*T1
- 第四次等待 8*T1
- ……直到达到上限
如果一直没有任何响应,最终会因超时而终止事务。
2. 收到 1xx 临时响应后会怎样
只要 UAC 收到任意 1xx 响应,例如 100 Trying 或 180 Ringing,说明请求已经到达对端或至少到达了代理链路上的某个节点,此时 INVITE 请求本身的重传会停止。
这是个很重要的点:收到 1xx 不代表呼叫成功,只代表请求不必继续重发。
3. 最终响应后的 ACK 为什么特殊
INVITE 最容易让人困惑的点,是 ACK 的行为并不统一:
- 对 非 2xx 最终响应(如 486、408、500)的 ACK,属于 INVITE 事务的一部分
- 对 2xx 最终响应 的 ACK,不属于 INVITE 事务,而是由核心层直接生成,用于建立或确认对话状态
这也是为什么很多人抓包时会觉得:明明 INVITE 已经结束了,为什么 200 OK 还可能继续被重发?原因就在于对 2xx 的 ACK 不归原事务状态机管理。
六、非 INVITE 事务的重传规则
非 INVITE 请求包括 REGISTER、BYE、OPTIONS、CANCEL、MESSAGE 等。它们的事务处理相对简单,通常没有长时间振铃过程,也不涉及 2xx ACK 特殊逻辑。
1. 非 INVITE 请求的发送侧重传
在 UDP 下,客户端事务发送请求后也会重传,间隔同样从 T1 开始指数增长,但通常在增长到 T2 后不再继续翻倍,而是以 T2 为上限继续等待。
这类设计体现的是:
- 前期快速探测网络是否通畅
- 后期避免过度打爆链路
2. 最终响应后进入 Completed / Terminated
非 INVITE 事务收到最终响应后,一般不会像 INVITE 那样再额外处理一个特殊的 ACK 分支。它的状态机更直接,因此 Timer 设计也更整齐。
七、常见 Timer 分别做什么
如果只记基础参数 T1、T2、T4,还是不够。SIP 真正的行为控制,落在具体 Timer 上。下面按“最常遇到”的思路来理解。
1. Timer A
用于 INVITE 客户端事务 的请求重传计时器。走 UDP 时启用,从 T1 开始,每次超时后指数退避。
作用: 如果 INVITE 没有收到任何响应,就继续重发。
2. Timer B
用于 INVITE 客户端事务总超时控制。即使 Timer A 一直在重传,也不能无限持续下去,Timer B 到点后,事务就判定超时失败。
可理解为: “这次呼叫建立尝试最多等多久,超过就别等了。”
3. Timer D
用于 INVITE 客户端事务在收到非 2xx 最终响应后保留 Completed 状态一段时间,主要是为了吸收可能重复到来的最终响应,防止旧消息扰乱状态。
4. Timer E
用于 非 INVITE 客户端事务 的请求重传定时器。它和 Timer A 的思路类似,但适用于 REGISTER、BYE 等非 INVITE 请求。
5. Timer F
用于 非 INVITE 客户端事务总超时控制。如果请求一直等不到最终响应,Timer F 到期后事务失败。
6. Timer K
用于非 INVITE 客户端事务在收到最终响应后短暂停留,吸收网络中可能残留的重复消息。
7. Timer G / H / I
这几个主要出现在 INVITE 服务端事务 中:
- Timer G:服务端在发送非 2xx 最终响应后,如果没有收到 ACK,会周期性重发该最终响应
- Timer H:限制服务端等待 ACK 的总时长
- Timer I:ACK 到来后,或状态收敛后,用于等待网络中残留消息自然消失
这套机制解释了一个经典现象:如果 UAC 没发 ACK,UAS 可能会重复发送 486 Busy Here 或 500 Server Internal Error 等非 2xx 最终响应。
8. Timer J
主要用于 非 INVITE 服务端事务,帮助服务端在发送最终响应后保留状态一段时间,以处理潜在重复请求。
八、为什么会出现“重复响应”或“重复请求”
初学者抓包时常会困惑:为什么同一个 INVITE 发了多次?为什么 200 OK 连续回来几次?这不是系统坏了,而往往是 SIP 为应对不可靠传输做出的正常行为。
典型场景 1:请求丢了,所以重传
UAC 发出 INVITE,但网络中途丢包。Timer A 到期后再次发送。这时抓包会看到同一个 INVITE 多次出现,分支参数可能相同,Call-ID 也相同。
典型场景 2:响应丢了,所以服务端重发最终响应
UAS 发出了 486 Busy Here,但 ACK 没到。服务端事务会认为客户端可能没收到,因此重复发这个最终响应,直到收到 ACK 或超时放弃。
典型场景 3:200 OK 被重发
当 INVITE 收到 2xx 最终响应后,对应的 ACK 不属于原 INVITE 事务。如果 ACK 没到,对端可能继续发送 200 OK。因此抓包中经常看到多个 200 OK,对应一个迟迟没有回来或丢失的 ACK。
九、UDP 与 TCP / TLS 下的差异
很多 Timer 是为 UDP 设计的。到了 TCP 或 TLS 传输时,情况会不同:
- 底层传输层已经提供可靠性
- 请求级重传不再是主要问题
- 部分与报文重发相关的 Timer 不再启用或意义减弱
但要注意:传输可靠 ≠ 业务一定成功。即使在 TCP 下,SIP 事务层仍然需要超时控制,因为对端应用可能没处理、代理链路可能卡住、上层状态机可能没有推进。
十、从现象理解超时:几个常见问题
1. 一直收不到任何响应
可能原因:
- 目标地址错误
- 网络不通
- 防火墙丢弃
- 请求未送达 SIP 服务端
表现:
- 客户端持续重传请求
- 最终触发 Timer B 或 Timer F 超时
2. 收到 100 Trying 后长时间没有最终响应
说明请求已经到了某个处理节点,但后续路由、定位、振铃或业务执行没有完成。此时请求重传通常已经停止,但呼叫仍然可能最终失败。
3. 收到 486 / 500 等最终响应却反复出现
重点检查 ACK 是否正确发出、是否被对端收到。很多问题并不是“服务端重复发错了”,而是 ACK 丢失导致的合理重发。
4. 200 OK 一直重复
优先怀疑 ACK 对 2xx 没有正确返回,或返回路径有问题。
十一、抓包时该怎么看重传
看 SIP 抓包时,不要只盯着“有没有重复报文”,而要沿着状态变化去看:
- 先确认传输层:UDP 还是 TCP
- 确认请求方法:INVITE 还是非 INVITE
- 看第一次响应出现在哪个时间点
- 看重传间隔是否呈指数退避
- 看最终响应后是否有 ACK
- 看重复响应是 2xx 还是非 2xx
如果能结合 Via branch、CSeq、Call-ID、From/To tag 一起看,基本就能判断该报文属于哪个事务、哪一阶段。
十二、工程上应该怎么理解这套 Timer 体系
如果从工程视角总结,SIP Timer 并不是单纯为了“重发几次”而设计,而是为了完成三件事:
- 快速发现报文可能丢失
- 避免事务无限等待
- 在状态收敛后安全清理上下文
也就是说,重传只是表面现象,真正核心是状态机控制。
十三、总结
SIP 的重传机制与 Timer 体系之所以让人觉得复杂,是因为它同时面对了 UDP 不可靠传输、呼叫建立流程长、最终响应分支复杂等多个问题。真正理解它,可以抓住下面几个核心点:
- SIP 重传主要发生在事务层
- INVITE 与 非 INVITE 的处理逻辑不同
- T1、T2、T4 是基础参数,不是全部逻辑
- Timer A/B/E/F 更多控制客户端请求重传与超时
- Timer G/H 等更多控制服务端最终响应与 ACK 等待
- 2xx 的 ACK 特殊,这是理解 INVITE 事务的关键
如果把这套机制想明白,再去看抓包,你会发现很多“奇怪的重复报文”其实都非常合理。
下一步如果继续深挖,最适合接着读的主题通常是:
- SIP 事务层与对话层的区别
- 一次完整 SIP 呼叫流程抓包分析
- SIP 在 NAT 环境下的典型问题与解决思路