📹 第一章 流媒体与RTSP/RTP/RTCP简介
📄 一句话概括

通用头一共就七个常用的,其中只有 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 秒实验

  1. 抓一次 OPTIONS 请求和响应,把两边的 CSeq 对一下,看是不是一样。
  2. 造一个 Date 头,把 GMT 换成 UTC,再拿严格的时间解析器读一次。
  3. 看你手上设备的 Server 头写的是什么,能不能从里面认出型号。

常见误区

  1. 以为 CSeq 可以从 0 开始。 0 是保留值,实际实现从 1 开始。从 0 开始有的设备直接不回。
  2. 以为 Date 头没人看所以随便写。 大多数设备不看, 但严格实现会因格式不符拒收——而且这个错极难发现。
  3. 以为 Server 头只是装饰。 它泄露软件版本,是安全扫描的入口; 同时它也是你快速识别陌生设备型号的线索。
自测三问(点开对答案) 1. 七个通用头里哪个错了就完全没法工作? —— CSeq。 响应必须原号返回,否则客户端配不上号,把响应当别人的丢弃,表现为「两边都正常但程序说超时」。 2. Date 头末尾为什么必须写 GMT? —— RFC 1123 规定的固定格式,解析器按位置读。写 UTC 虽然时区相同,但严格实现会在这一步失败。 3. 重发同一条请求时 CSeq 要不要加一? —— 不要。重传要保持原号,服务端才能识别出这是重复请求而不是新请求。

下一节讲连接:一条 TCP 撑一场播放,到底省了多少个包。

相关笔记