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 秒实验
- 算一下:把 T1 改成 1 秒,总超时变成多少?(64 秒)
- 想一下:大规模部署为什么改用 TCP?
- 想一下:为什么要对重传的报文做去重?
常见误区
- 以为「SIP 只能用 UDP」。 规范允许 TCP,大规模部署常用 TCP。
- 以为「重传间隔固定更可靠」。 固定间隔会加重拥塞,所以要指数退避。
- 以为「超时是几秒钟的事」。 判死是 32 秒,改完配置要等半分钟。
自测三问(点开对答案)
1. T1、T2、64 乘 T1 分别是多少? —— **0.5 秒、4 秒、32 秒**。 2. 为什么用指数退避而不是固定间隔? —— **丢包多因拥塞,固定间隔会继续加压**。 3. 不做去重会出什么事? —— **重复的 INVITE 会建出两条会话**,一次点播出两路流。下一节进编码:国标最基础的那条规则——所有东西都有 20 位编号。
- ☐ (本节完,下一节 10.05.01 统一编码规则)