SIP理论篇11:重传机制与 Timer 全解析

SIP 是一个基于文本的应用层信令协议,看上去和 HTTP 很像,但它面对的网络环境比传统 Web 请求更复杂:终端可能在 UDP 上通信,链路可能丢包,响应可能延迟到达,代理服务器可能发生分叉转发。因此,SIP 不能只依赖“发一次、等结果”,而必须设计一套完整的重传机制定时器(Timer)体系来保证请求、响应与事务状态能够被正确推进。

很多人在学 SIP 时,只记住了几个结论:

  • UDP 下会重传,TCP 下通常不需要
  • INVITE 和非 INVITE 的行为不一样
  • T1、T2、T4 很重要,但总是记不清

这篇文章不只停留在“背规则”,而是从为什么需要重传SIP 事务层如何工作各类 Timer 的职责实际超时现象如何表现几个角度,把这套机制讲清楚。

一、为什么 SIP 需要重传机制

如果 SIP 全部跑在 TCP 上,那么很多可靠性问题可以交给传输层处理。但现实并不是这样。SIP 在大量部署里长期依赖 UDP,原因包括:

  • 实现简单,开销小
  • 适合短报文交互
  • 早期设备与网络环境广泛支持
  • 和实时通信场景匹配较好

但 UDP 的问题也很明显:

  • 不保证报文送达
  • 不保证顺序
  • 不保证不重复
  • 不提供连接状态

于是 SIP 自己必须解决两个问题:

  1. 请求发出去后,如果对方没收到怎么办?
  2. 响应回来了,但原始发送方没收到怎么办?

这就是 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 抓包时,不要只盯着“有没有重复报文”,而要沿着状态变化去看:

  1. 先确认传输层:UDP 还是 TCP
  2. 确认请求方法:INVITE 还是非 INVITE
  3. 看第一次响应出现在哪个时间点
  4. 看重传间隔是否呈指数退避
  5. 看最终响应后是否有 ACK
  6. 看重复响应是 2xx 还是非 2xx

如果能结合 Via branch、CSeq、Call-ID、From/To tag 一起看,基本就能判断该报文属于哪个事务、哪一阶段。

十二、工程上应该怎么理解这套 Timer 体系

如果从工程视角总结,SIP Timer 并不是单纯为了“重发几次”而设计,而是为了完成三件事:

  1. 快速发现报文可能丢失
  2. 避免事务无限等待
  3. 在状态收敛后安全清理上下文

也就是说,重传只是表面现象,真正核心是状态机控制

十三、总结

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 环境下的典型问题与解决思路

上一篇