📹 第十章 GB/T 28181协议原理
📄 一句话概括

SIP 默认跑 UDP,所以它自己带了一套指数退避的重传。 T1 是 0.5 秒,翻倍到 4 秒封顶,累计 32 秒还没回应就判死。

📝 本节和前面几节的关系

上一节把四层协议摆开了。这一节只管最底下那一件:字节怎么送到对面。 Demo 把重传的时间轴一秒一秒算出来,看「64 乘 T1」那条经验规则是怎么来的。

一、为什么是 UDP,而不是 TCP

SIP 的设计初衷是「像打电话一样快」,而 TCP 的三次握手要先花一个往返。 在一个「注册、心跳、点播」都很频繁的系统里,这一个往返乘上去就是可观的延迟。 所以 SIP 默认走 UDP,重传自己做。 代价是它必须自己实现「超时、重传、去重」这三件事——而这三件事 TCP 都替你做了。 国标没有强制 UDP: 规范里写明「信令可用 UDP 或 TCP」,而实践中的选择很实际—— 小规模用 UDP 省延迟,大规模(几千路)用 TCP 省重传风暴。 因为 UDP 一旦丢包,每一路都在重传,几千路同时重传就是自己打自己。

二、时间轴:0.5、1、2、4、4、4

重传规则只有三条:首次间隔 T1(默认 0.5 秒)、每次翻倍、上限 T2(默认 4 秒)。 而总超时是「64 乘 T1」,也就是 32 秒。 为什么是「翻倍」而不是「固定间隔」:因为丢包往往是拥塞造成的, 固定间隔重传只会往已经拥塞的链路上继续加压。 翻倍等于「越不顺利越退让」——这个思路和 TCP 的拥塞退避是同一个。 为什么上限是 4 秒:再大就不像「实时」了。 一个点播请求等 4 秒还没回应,用户早就以为卡死了。 32 秒这个总数的意义:它是「判死」的时刻,到了这个点,事务被丢弃、上层收到超时。 所以「注册不上」这个现象,最快也要 32 秒才会报出来—— 这也是为什么现场调试时,改完配置要等半分钟才看结果。

三、动手 Demo:SIP 为什么默认用 UDP,超时又是怎么算的

从 T1 开始,每次翻倍、4 秒封顶,把每次重传的时刻和累计等待算出来,直到 32 秒判死。

T1, T2 = 0.5, 4.0
t, total, n = T1, 0.0, 0
while total + t <= 32:
    n += 1
    print(n, "%.2f" % t, "%.2f" % total)
    total += t
    t = min(t * 2, T2)
print("共重传", n, "次,第 32 秒判死")

实跑输出:

--------------------------------------------------------------------
SIP 为什么默认用 UDP,超时又是怎么算的
--------------------------------------------------------------------
**SIP 默认跑 UDP**,而 UDP 会丢包,**所以 SIP 自己带了一套重传:起始 T1,每次翻倍,上限 T2。**
**这一节把时间轴算出来。**

  重传                       间隔            累计等多久
  --------------------------------------------------------
  **第 1 次**                **0.50 秒**     **0.00 秒**
  **第 2 次**                **1.00 秒**     **0.50 秒**
  **第 3 次**                **2.00 秒**     **1.50 秒**
  **第 4 次**                **4.00 秒**     **3.50 秒**
  **第 5 次**                **4.00 秒**     **7.50 秒**
  **第 6 次**                **4.00 秒**     **11.50 秒**
  **第 7 次**                **4.00 秒**     **15.50 秒**
  **第 8 次**                **4.00 秒**     **19.50 秒**
  **第 9 次**                **4.00 秒**     **23.50 秒**
  **第 10 次**               **4.00 秒**     **27.50 秒**
  **第 11 次(不再发)**     —               **32.00 秒**

**一共重传 10 次,第 32 秒判死。**
**这就是「64 乘 T1」那条规则的来历**——**T1 是 0.5 秒,乘 64 就是 32 秒。**
**为什么是「翻倍」而不是「固定间隔」**:**因为丢包往往是拥塞造成的**,
**固定间隔重传只会往拥塞的链路上继续加压**。
**翻倍等于「越不顺利越退让」,这个思路和 TCP 的退避是同一个。**

**实际部署里还有一个更常见的坑**:

  现象                   原因
  ------------------------------------------------------
  **注册总是失败**       **NAT 后面的地址被写进了 Via**
  **能注册不能点播**     **媒体端口没做映射**
  **过一会儿就掉线**     **心跳没发,或 Expires 太短**
  **日志里全是重传**     **对端回了 ICMP 不可达**

**第四行值得多说一句**:**SIP 收到 ICMP 不可达时会直接判事务失败**,
**而很多防火墙默认回一个不可达**,**于是「网络明明是通的,注册就是上不去」。**
**排查这类问题的标准动作是抓包看有没有 ICMP**。

一共十次重传,前三次间隔是 0.5、1、2,之后一直 4 秒。 累计到第十次是 27.5 秒,再加 4 秒就超过 32,所以停在这里。 这个时间轴解释了一个常见现象: 「改了配置没反应」——因为第一个事务还在重传,要等它判死,新的注册才会重来。 所以现场调 SIP,改完至少等半分钟。 还有一条更隐蔽的:重传的报文和原报文一模一样,Seq 也一样, 所以接收端必须做「去重」——收到重复的 INVITE 要回同样的 200,而不是再建一个会话。 不做去重就会出现「一次点播、两路流」的怪事。

30 秒实验

  1. 算一下:把 T1 改成 1 秒,总超时变成多少?(64 秒)
  2. 想一下:大规模部署为什么改用 TCP?
  3. 想一下:为什么要对重传的报文做去重?

常见误区

  1. 以为「SIP 只能用 UDP」。 规范允许 TCP,大规模部署常用 TCP。
  2. 以为「重传间隔固定更可靠」。 固定间隔会加重拥塞,所以要指数退避。
  3. 以为「超时是几秒钟的事」。 判死是 32 秒,改完配置要等半分钟。
自测三问(点开对答案) 1. T1、T2、64 乘 T1 分别是多少? —— **0.5 秒、4 秒、32 秒**。 2. 为什么用指数退避而不是固定间隔? —— **丢包多因拥塞,固定间隔会继续加压**。 3. 不做去重会出什么事? —— **重复的 INVITE 会建出两条会话**,一次点播出两路流。

下一节进编码:国标最基础的那条规则——所有东西都有 20 位编号。

相关笔记