通用头一共就七个常用的,其中只有 CSeq 是缺了就活不成的。 Date 头看着最没用,但格式写错能让严格的客户端直接拒收。
前面讲了地址、实体、会话、参数,这一节把剩下的通用头收拾干净。 这些头是 RTSP 和 HTTP 最像的部分,所以看着眼熟,但用法上有一处关键差异——CSeq 的存在。 下一节讲连接,再下一节讲扩展,通用头这一节是它们的地基。
一、七个通用头,各管一件事
通用头是「请求和响应都可能出现」的头,和实体头、请求头分开算。
| 头 | 管什么 | 缺了会怎样 |
|---|---|---|
| CSeq | 请求序号,响应原号返回 | 客户端配不上号,当超时处理 |
| Date | 服务端时间,RFC 1123 格式 | 基本没人看,但格式错会被拒 |
| Server | 服务端软件名与版本 | 无所谓,但泄露信息 |
| Session | 会话号与超时 | 后续每条请求被拒(454) |
| Transport | 端口与传输方式 | 数据发到没人听的地方 |
| User-Agent | 客户端软件名 | 有的设备靠它做兼容分支 |
| Bandwidth | 客户端可用带宽 | 少数设备据此选码流 |
七个里只有前五个是天天见的。Bandwidth 用得很少,但有的设备真会拿它挑主码流还是子码流。
User-Agent 有个隐性用途:有些设备的兼容逻辑是「看到某个播放器的名字就走另一套参数」,
这时候 User-Agent 写错就会出现「同样的请求别人能播我不能」。
二、CSeq:唯一不能错的头
CSeq 的作用只有一个:把响应和请求对上。 但它是整个协议里唯一「错了就完全没法工作」的字段。
| 规矩 | 说明 |
|---|---|
| 从 1 开始 | 0 保留,不要用 |
| 每条新请求加一 | 重发可以保持原号 |
| 响应必须原号返回 | 不能自己编号 |
| 同一连接上单调递增 | 这也是不能流水线的原因之一 |
错误的表现很隐蔽:请求发出去了,响应也回来了,但客户端认为超时——因为回来的 CSeq 对不上, 被当成别人的响应丢弃。 抓包时看两边都正常,唯独程序说超时,八成就是这里。
有一种情况会故意用同一个 CSeq:超时重传。 重发同一条请求时保持原号,服务端才能识别出「这是重复的」而不是新请求。
三、Date:格式比内容重要
Date 的格式由 RFC 1123 规定,长这样:
Date: Sat, 24 Oct 2026 16:30:00 GMT
四段,每段都有讲究: 星期是英文三字母缩写加逗号; 日是两位、不足补零; 月份是英文三字母缩写; 末尾必须是 GMT,不能写 UTC 也不能写 +0800。
为什么这么严:解析器多是按固定格式逐段读的,多一个字母就对不齐。 尤其是把 GMT 写成 UTC 这个错特别常见——因为它们指的是同一个时区,人看着没错,解析器会挂。
实测里绝大多数设备都不看这个头,但写对了没坏处;反过来你写服务端时,格式一定要按规范来。
四、Server 与安全
Server 头会暴露服务端软件名和版本,比如:
Server: Hikvision RTSP Server 1.2.3
这在安全上是个小漏洞:知道版本就能查对应的已知漏洞。
所以不少产品会把它改成 Server: RTSP Server 或者干脆不发。
但调试时它很有用:一眼能看出对面是什么设备、什么固件系列。 对接陌生设备时,先抓一次 OPTIONS 或 DESCRIBE,看 Server 头,比翻说明书快。
五、动手 Demo:通用头就那几个,配对全靠 CSeq
这段列出七个通用头,再按 RFC 1123 真造一个 Date 头。 重点看最后那行生成的字符串:星期、日、月、GMT 各是什么格式。
import datetime
now = datetime.datetime.now(datetime.timezone.utc)
stamp = now.strftime(u"%a, %d %b %Y %H:%M:%S GMT")
print(stamp) # Sat, 24 Oct 2026 16:30:00 GMT
# 反例:把 GMT 写成 UTC,看着没错,解析器会挂
print(now.strftime(u"%a, %d %b %Y %H:%M:%S UTC"))
实跑输出:
--------------------------------------------------------------------
通用头就那几个,配对全靠 CSeq
--------------------------------------------------------------------
**RTSP 的头分两类:通用头(请求响应都能有)和实体头(只管消息体)。**
通用头里最要紧的是 CSeq——**它是唯一把请求和响应绑在一起的线索**。
一条真实响应长这样:
RTSP/1.0 200 OK
CSeq: 2
Date: Sat, 24 Oct 2026 16:30:00 GMT
Server: Hikvision RTSP Server
Content-Type: application/sdp
Content-Length: 341
**逐个说它们管什么:**
头 管什么 写错的后果
----------------------------------------------------------------------------
CSeq 请求号,响应原号返回 对不上号,客户端直接丢弃
Date 服务端当前时间,RFC 1123 基本没人看
Server 服务端软件名与版本 泄露版本,安全上要遮
Session 会话号与超时 后面每条请求被拒(454)
Transport 端口与传输方式 数据发到没人听的端口
Content-Type 实体格式 客户端不认这个 SDP
Content-Length 实体字节数 少了等不齐,多了多读
**Date 头按 RFC 1123 的格式,现在这一刻应该是:**
Date: Sat, 24 Oct 2026 08:29:36 GMT
注意三点:星期和月份是英文三字母缩写、日不足两位补零、末尾必须是 GMT 而不是 UTC。
**差一个字母,严格的客户端就会解析失败**——这是跨语言对接里最烦的一类小坑。
三个格式符分别给英文星期缩写、英文月份缩写、补零的两位日——正好对上 RFC 1123 的要求。
注意「补零的两位日」这一项:1 号要写成 01,写成 1 就对不齐。
最后那个反例值得看一眼:它输出的字符串人看着完全正常,UTC 和 GMT 在时间上是一回事。
但严格按 RFC 1123 解析的代码会在这一步失败——因为它期望的位置上是 GMT 三个字母。
这类「人看着对、机器看着错」的坑在协议对接里特别多:还有 Content-Length 少数两个字节、 CSeq 从 0 开始、行尾用 LF 而不是 CRLF。 共同点是都不影响阅读,只影响解析——所以只能靠规范,不能靠眼睛。
30 秒实验
- 抓一次 OPTIONS 请求和响应,把两边的 CSeq 对一下,看是不是一样。
- 造一个 Date 头,把 GMT 换成 UTC,再拿严格的时间解析器读一次。
- 看你手上设备的 Server 头写的是什么,能不能从里面认出型号。
常见误区
- 以为 CSeq 可以从 0 开始。 0 是保留值,实际实现从 1 开始。从 0 开始有的设备直接不回。
- 以为 Date 头没人看所以随便写。 大多数设备不看, 但严格实现会因格式不符拒收——而且这个错极难发现。
- 以为 Server 头只是装饰。 它泄露软件版本,是安全扫描的入口; 同时它也是你快速识别陌生设备型号的线索。
自测三问(点开对答案)
1. 七个通用头里哪个错了就完全没法工作? —— CSeq。 响应必须原号返回,否则客户端配不上号,把响应当别人的丢弃,表现为「两边都正常但程序说超时」。 2. Date 头末尾为什么必须写 GMT? —— RFC 1123 规定的固定格式,解析器按位置读。写 UTC 虽然时区相同,但严格实现会在这一步失败。 3. 重发同一条请求时 CSeq 要不要加一? —— 不要。重传要保持原号,服务端才能识别出这是重复请求而不是新请求。下一节讲连接:一条 TCP 撑一场播放,到底省了多少个包。
- ☐ (本节完,下一节 01.04.07 RTSP连接)