RTSP 默认用持久连接:连一次,整场播放的信令都走这一条。 每条请求重连的话,光握手挥手就要九个包,比请求本身还重。
上一节把通用头讲完了,这一节讲这些报文跑在什么连接上。 连接这件事看着底层,但它直接决定了「延迟」和「断线怎么发现」—— 后者是监控里最难查的一类问题。
一、一条连接的完整生命周期
从建立到断开,一共七个动作、九个包:
| 阶段 | 包数 | 谁发起 |
|---|---|---|
| 三次握手 | 3 | 客户端 |
| 请求与响应 | 2 | 客户端发请求,服务端回 |
| 四次挥手 | 4 | 双方都可能发起 |
这九个包里,只有两个是「干活」的,其余七个都是开销。 所以一条连接用得越久,摊到每个请求上的开销越小。
这就是持久连接存在的全部理由——不是为了省资源,是为了省往返。
二、省了多少:算一遍
假设一场播放要发 N 条信令:
| 做法 | 包数 | 说明 |
|---|---|---|
| 每条重连 | N × 9 | 完全不能接受 |
| 持久连接 | 3 + N × 2 + 4 | 握手挥手只付一次 |
N=5 时:45 包 vs 17 包,省 62%。 N=20 时:180 包 vs 47 包,省 74%。 N=60 时:540 包 vs 127 包,省 76%。
包数只是账面上的一半,真正值钱的是往返次数。 走公网一次往返按 30 毫秒算,每条请求多付一次往返, 60 条就是 1.8 秒——用户能明显感觉到「点了半天才有反应」。
三、连接什么时候会断
四种断法,前两种是正常的,后两种要靠自己发现:
| 断法 | 谁发起 | 怎么发现 |
|---|---|---|
| 发完 TEARDOWN | 客户端 | 自己知道 |
| Session 超时 | 服务端 | 下条请求回 454 |
| 设备重启 | 对方 | TCP 可能还没报错 |
| 网线拔了 | 网络 | 要等 TCP 超时 |
后两种最阴:TCP 连接在协议栈看来还是「已建立」,你往里面写数据也不报错(数据进了发送缓冲), 但对面永远不回。 默认的 TCP 重传要试好几分钟才放弃,这期间程序毫无察觉。
所以播放器必须有应用层看门狗:多久没收到任何响应/数据,就主动断掉重连。 这个时长要比业务能忍的极限短——监控一般给 5 到 10 秒。
四、TCP 还是 UDP
这里的「TCP 还是 UDP」问的不是媒体,是信令。
| TCP | UDP | |
|---|---|---|
| RTSP 信令 | 唯一选择 | 不支持 |
| RTP 媒体 | 可选(穿墙时用) | 默认 |
信令只能走 TCP:它要求可靠、有序、不丢,而 UDP 一样都不保证。 媒体默认走 UDP:它要求快,宁可丢也不要等。
这条分工是 RTSP 架构的根,所有「信令通了但没画面」「画面有但控不了」的问题, 都是因为这两条通道的独立性。
五、动手 Demo:一条 TCP 撑一场播放,比每次重连省多少
这段把「每条重连」和「持久连接」的包数算出来,对比省了多少。 注意省的比例随请求数上升而上升——因为固定开销被摊薄了。
for n in (5, 20, 60):
short = n * 9 # 每条请求重连:9 个包
keep = 3 + n * 2 + 4 # 持久:握手 + 请求响应 + 挥手
print(n, short, keep, (short - keep) * 100.0 / short)
实跑输出:
--------------------------------------------------------------------
一条 TCP 撑一场播放,比每次重连省多少
--------------------------------------------------------------------
**RTSP 默认用持久连接:连一次,整场播放的几十条信令都走这一条。**
要是每条请求都重连,光握手挥手就把开销吃光了。
**先数一次 TCP 连接的包:**
阶段 包数 谁发给谁
------------------------------------------------
三次握手 3 SYN/SYN+ACK/ACK
请求与响应 2 请求 1 个,响应 1 个
四次挥手 4 FIN/ACK/FIN/ACK
合计 9 —
5 条请求:短连接 45 个包,持久连接 17 个包,**省掉 28 个(62%)。**
20 条请求:短连接 180 个包,持久连接 47 个包,**省掉 133 个(74%)。**
60 条请求:短连接 540 个包,持久连接 127 个包,**省掉 413 个(76%)。**
这里的账只算了包数。**实际更值钱的是「少一次往返」**:
走公网往返按 30 毫秒算,短连接每条请求多付 30 毫秒,60 条就是 1.8 秒。
**连接什么时候断:**
情形 连接状态
---------------------------------------------------
TEARDOWN 之后 客户端主动断
Session 超时 服务端主动断
设备重启/网络断 谁都收不到,只能靠保活发现
**第三条最阴**:TCP 已经断了,但你的程序还在 sleep 等包,看起来一切正常,
实际上画面三分钟没动过。
所以播放器必须有「多久没收到包就重连」的看门狗。
三个比例分别是 62%、74%、76%,越来越高但趋于上限。 上限是多少? 算一下就明白:N 很大时短连接是 9N,持久是 2N, 极限省 78%——因为三次握手加四次挥手那 7 个包摊到无穷多条请求上就不值钱了。
但包数不是重点,往返次数才是。 短连接每条请求多一次握手往返加一次挥手段,公网 30 毫秒一次的话,60 条请求就是三秒多的纯等待。 这就是为什么所有 RTSP 客户端默认都用长连接。
还有一个更实际的考虑:设备端的连接数是有限的。 一台摄像机可能只允许 4 到 16 条并发 RTSP 连接,频繁重连很容易撞上「连接数已满」, 表现为随机失败。 这类问题的特征是「有时好有时坏」,因为要看上一批连接释放了没有。
30 秒实验
- 抓一次完整的播放,数一下整场用了几条 TCP 连接。
- 算一下你的场景(比如 20 条信令)用持久连接能省多少个包。
- 把网线拔掉,看播放器多久才发现——这个时间就是它的看门狗阈值。
常见误区
- 以为每个请求都要新建连接。 RTSP 默认是持久连接,整场播放的信令都走同一条。
- 以为 TCP 断了程序会立刻知道。 拔网线的情况下,协议栈可能几分钟才报错, 应用层必须自己设看门狗。
- 以为信令也能走 UDP。 不能。RTSP 只跑 TCP,UDP 那条路是 RTP 媒体专用的。
自测三问(点开对答案)
1. 持久连接省下来的主要是包数还是往返次数? —— 往返次数。包数省 60% 到 78%,但公网上每次往返 30 毫秒的等待才是真正能感觉到的部分。 2. 拔掉网线,为什么程序往往不会立刻发现? —— TCP 连接在协议栈看来还是已建立状态,写数据进发送缓冲不报错,重传要试好几分钟才放弃。 所以需要应用层看门狗。 3. 频繁重连还有什么隐性代价? —— 设备的并发连接数有上限(常见 4 到 16 条),重连太快容易撞上「连接数已满」,表现为随机失败。下一节讲扩展:Require 和 Supported 怎么谈条件,为什么是 551 不是 501。
- ☐ (本节完,下一节 01.04.08 RTSP扩展)