Transport 头决定「数据用什么搬、搬到哪个端口」。 RTP 用偶数端口、RTCP 用下一个奇数端口,差这一是为了配对。
上一节讲会话,这一节讲会话里最重要的一样参数:传输。 这一节是实战里改得最多的一个头——穿透 NAT、换 TCP、开组播,全是在改它。
一、Transport 头的完整形态
最长的一种写法,五段用分号隔开:
Transport: RTP/AVP;unicast;client_port=8000-8001;server_port=9000-9001;ssrc=1A2B3C4D
| 段 | 意思 |
|---|---|
| RTP/AVP | 用 RTP 传,负载类型按 AVP 约定 |
| unicast | 单播,一对一 |
| client_port | 客户端收 RTP/RTCP 的端口对 |
| server_port | 服务端发 RTP/RTCP 的端口对 |
| ssrc | 这一路流的同步源标识 |
头四段几乎总在,ssrc 不一定:多数设备在 SETUP 的响应里才告诉你,请求里不用写。
二、为什么成对、为什么一奇一偶
一路流要两个通道:RTP 搬数据,RTCP 送统计。 所以端口也是两个,而且必须挨着,差 1。
| 端口 | 用途 | 端口号的奇偶 |
|---|---|---|
| 8000 | 收 RTP | 偶数 |
| 8001 | 收 RTCP | 奇数 |
RFC 3550 的建议是 RTP 用偶数端口,这样接收方拿到一个端口号, 按「& 1」就能判断这个包该交给 RTP 还是 RTCP。 差 1 的另一个作用是「一眼能配对」:看到 8000 和 8001,就知道它们是同一路的两个通道。
所以给一路流分配端口时,要连分两个——只分一个的话,RTCP 没地方去,丢包统计就永远收不到。
三、换成 TCP 就全不一样
UDP 穿不过防火墙时,改成 TCP 传。 这时不再有端口对,改成「通道号」。
| 写法 | 什么时候用 | 代价 |
|---|---|---|
| RTP/AVP;unicast;client_port=8000-8001 | 局域网,最常用 | 要开 UDP |
| RTP/AVP/TCP;unicast;interleaved=0-1 | 穿透 NAT/防火墙 | TCP 重传会加剧延迟 |
| RTP/AVP;multicast;ttl=16 | 一对多分发 | 要交换机支持组播 |
interleaved=0-1 的意思是「RTP 走 0 号通道,RTCP 走 1 号通道」,
两个通道挤在同一条 TCP 连接里,靠通道号区分。
TCP 的代价要说清楚:TCP 保证不丢,代价是丢了要重传,重传期间后面的包要排队。 对实时视频来说,一个迟到五秒的包毫无价值,但 TCP 还是会把它送来,占着带宽还堵着后面的新数据。 这就是「宁可 UDP 丢包,不要 TCP 阻塞」的由来。
四、组播:一份数据多人收
multicast 是「发一份,谁想收谁加入」。
这和单播的差别在网络层,不在协议层。
| 单播 | 组播 | |
|---|---|---|
| 发几份 | 每个接收者一份 | 只发一份 |
| 交换机负担 | 按人数翻倍 | 一份 |
| 需要什么 | 普通网络 | 交换机支持 IGMP |
| 典型场景 | 点开一个画面 | 大屏轮巡 |
组播的 ttl 参数是「能过几跳路由器」,16 是个保守值。 组播跨网段几乎一定失败,因为大部分企业网不配组播路由。
所以组播在监控里的实际用法是「同一个交换机下的多客户端」, 跨网段还是老老实实单播或者走平台转发。
五、动手 Demo:Transport 头里那对端口,必须一奇一偶
这段把一条完整的 Transport 头逐段拆开,再算端口对的关系。 顺便验证「偶数给 RTP、奇数给 RTCP」这条约定。
th = u"RTP/AVP;unicast;client_port=8000-8001;" \
u"server_port=9000-9001;ssrc=1A2B3C4D"
parts = th.split(u";")
for p in parts[2:]:
k, v = p.split(u"=", 1)
T([k, v, why], [0, 0, 0])
# 端口对:偶数给 RTP,奇数给 RTCP
for port in (8000, 8001, 9000, 9001):
print(port, port & 1) # 0 表示 RTP,1 表示 RTCP
实跑输出:
--------------------------------------------------------------------
Transport 头里那对端口,必须一奇一偶
--------------------------------------------------------------------
**SETUP 的 Transport 头决定「数据用什么搬、搬到哪个端口」。**
最常用的一种:`RTP/AVP;unicast;client_port=8000-8001`
逐段拆:
段 值 意思
-----------------------------------------------------------------
传输协议 RTP/AVP RTP over UDP,负载按 AVP 约定
单播/组播 unicast unicast 一对一,multicast 一对多
客户端端口 8000-8001 偶数收 RTP,奇数收 RTCP
服务端端口 9000-9001 同理,服务端这一对
同步源 1A2B3C4D 标记这一路流的身份
**为什么必须成对、必须一奇一偶:**
RTP 用 8000,RTCP 用 8001。**差 1,是为了让同一个会话的两条流能一眼配对。**
RFC 3550 明确建议 RTP 用偶数端口:奇数留给 RTCP,偶数留给 RTP,
这样实现方按「端口 & 1」就能判断这条包该交给谁。
**换成 TCP 传,Transport 头就不一样了:**
写法 意思
-------------------------------------------------------------------------
RTP/AVP/TCP;unicast;interleaved=0-1 RTP 走 0 通道,RTCP 走 1 通道
RTP/AVP;multicast;ttl=16 组播,16 跳的生存期
`interleaved` 是「交织通道号」——TCP 传的时候,RTP 和 RTCP 挤在同一条连接上,靠通道号区分,
所以不再需要端口对。
**穿透 NAT 时改 TCP 就是这个原因**:一条 TCP 出去,防火墙好放行得多。
port & 1 这个判断就是全部秘密:偶数是 0,奇数是 1。
接收方拿到一个 UDP 包,看它落在哪个端口,& 1 一下就知道该交给 RTP 还是 RTCP 处理。
这个设计在 1996 年就定了,简单到不需要任何表。
回到那条完整头,最后一段 ssrc 值得看一眼:1A2B3C4D。
它是这一路流的身份,RTCP 的统计报告靠它对应到具体哪一路。
多路流的时候,没有 ssrc 就分不清哪个报告说的是哪一路。
实战里最常改的就是 unicast 那一段:内网不用动,跨网段或过防火墙就换成 TCP 那种写法。
记住改的是两处:协议部分加 /TCP,端口对换成 interleaved。
只改一处是最常见的错——比如加了 /TCP 却还留着 client_port,设备会直接回 461。
30 秒实验
- 抓一次 SETUP,把 Transport 头抄下来,标出哪段是端口、哪段是 ssrc。
- 把 unicast 改成 multicast 试一次,看设备回什么(很多直接 461)。
- 改成 RTP/AVP/TCP;interleaved=0-1,看原来占的 UDP 端口是不是不再需要了。
常见误区
- 以为端口是服务端分配的。 客户端在 SETUP 里指定 client_port, 服务端照着发——这样 NAT 后面的客户端才收得到。
- 以为一路流只要一个端口。 要两个:RTP 一个、RTCP 一个,差 1。只分一个就收不到丢包统计。
- 以为 TCP 传更好。 TCP 保证不丢,但重传会堵住后面的新数据,对实时视频反而更糟。 只在穿不过防火墙时才用。
自测三问(点开对答案)
1. 为什么 RTP 用偶数端口、RTCP 用奇数? —— RFC 3550 的建议。 这样接收方按「端口 & 1」就能判断包该交给谁,不需要维护映射表,也方便一眼配对。 2. 改成 TCP 传时,Transport 头要改哪两处? —— 协议部分加 /TCP,端口对 client_port 换成 interleaved 通道号。只改一处会被回 461。 3. 组播为什么在监控里很少跨网段用? —— 需要沿途路由器都支持组播路由,多数企业网没配。实际只在同一交换机下多客户端时用。下一节讲通用头:CSeq 怎么配对、Date 为什么必须写 GMT。
- ☐ (本节完,下一节 01.04.06 RTSP信息)