(第六讲)RTMP握手流程
RTMP(Real-Time Messaging Protocol)的握手机制是建立客户端与服务器之间可靠连接的第一步。它发生在 TCP 三次握手之后,主要目的是协商协议版本、同步时间戳以及验证连接的合法性。
RTMP 握手主要分为简单握手(Simple Handshake)和复杂握手(Complex Handshake)两种模式。目前大多数现代流媒体服务器(如 Nginx-rtmp, SRS, Adobe Media Server)都支持或默认使用复杂握手以增强安全性或兼容性,但简单握手是协议的基础。
1. 握手基本流程概述
无论简单还是复杂握手,RTMP 握手都涉及三个固定大小的数据包交换,每个包大小为 1536 字节(除了第一个字节 C0/S0)。
- C0/C1: 客户端发送
- S0/S1/S2: 服务器响应
- C2: 客户端确认
标准握手序列:
- Client -> Server: 发送 C0 和 C1。
- Server -> Client: 收到 C1 后,发送 S0, S1 和 S2。
- Client -> Server: 收到 S1 后,发送 C2。
完成: 当 Client 收到 S2 且 Server 收到 C2 后,握手完成,后续可以开始传输 RTMP 消息块(Chunks)。
2. 数据包结构详解
C0 和 S0(协议版本标识)
大小: 1 字节
内容:
- 0x03: 表示 RTMP 版本 3(最常用,明文传输)。
- 0x06: 表示加密的 RTMPE(RTMP Encrypted)。
- 其他值通常被忽略或视为版本 3。
作用: 告知对端使用的 RTMP 协议版本。
C1 和 S1(关键数据交换)
大小: 1536 字节
结构:
- Time (4 bytes): 时间戳。通常是发送端的当前时间(毫秒级),用于后续计算网络延迟和同步。在简单握手中,这个值可以是任意值;在复杂握手中,它参与摘要计算。
- Zero (4 bytes): 全零字段(在简单握手中);在复杂握手中,这部分可能包含版本信息或随机数。
- Random Bytes (1528 bytes): 随机数据。
简单握手: 纯随机数,用于区分不同的连接会话。
复杂握手: 包含 Digest(摘要)和 Key(密钥)信息,用于 HMAC-SHA256 验证,防止中间人攻击或非法客户端连接。
C2 和 S2(回声确认)
大小: 1536 字节
结构:
- Time (4 bytes): 必须是对端发来的 S1 或 C1 中的时间戳。
- C2 中的 Time = S1 中的 Time。
- S2 中的 Time = C1 中的 Time。
Time2 (4 bytes): 本端之前发送的时间戳(可选,常用于计算往返延迟 RTT)。
Random Echo (1528 bytes): 必须原样返回对端 S1 或 C1 中的随机数据部分。
C2 的 Random Echo = S1 的 Random Bytes。
S2 的 Random Echo = C1 的 Random Bytes。
作用: 证明双方都正确接收并处理了对方的 C1/S1 包,确保连接的完整性和同步性。
3. 简单握手 vs 复杂握手
表格
| 特性 | 简单握手 (Simple Handshake) | 复杂握手 (Complex Handshake) |
|---|---|---|
| 定义来源 | Adobe RTMP 规范基础部分 | Adobe Flash Media Server 实现细节 |
| C1/S1 内容 | Time + Zero + 随机数 | Time + Version/Random + Digest + Key |
| 验证机制 | 无加密验证,仅靠随机数区分会话 | 使用 HMAC-SHA256 进行完整性校验 |
| 兼容性 | 所有 RTMP 服务器均支持 | 大多数现代服务器支持,旧设备可能不支持 |
| 用途 | 测试、内部网络、非安全场景 | 公共互联网直播、需要防篡改/鉴权的场景 |
复杂握手的关键点:
- Digest 位置: 在 1528 字节的随机区域中,通过特定算法确定 Digest 的偏移量(Offset)和长度(32字节)。
- Key 计算: 使用预共享密钥(FPKey)和数据进行 HMAC-SHA256 计算,生成签名。
- 验证: 接收方根据收到的数据重新计算 Digest,如果与收到的 Digest 匹配,则握手成功;否则断开连接。
4. 握手过程中的常见问题与 Broken Pipe
你之前提到的 java.net.SocketException: Broken pipe 在 RTMP 握手中可能由以下原因引起:
- 握手超时: 客户端发送 C1 后,服务器未在合理时间内响应 S1,或者客户端发送 C2 后服务器未确认。如果一方设置了 Socket 超时(setSoTimeout),超时后会关闭连接,另一方再写入数据就会报 Broken pipe。
- 版本不匹配: 客户端发送 C0 版本为 0x03,但服务器只支持其他版本(极少见),服务器可能直接关闭连接。
- 复杂握手验证失败: 如果服务器要求复杂握手,而客户端发送的是简单握手格式(或 Digest 计算错误),服务器会在收到 C1 后直接关闭连接,不再发送 S1/S2。客户端若继续尝试发送 C2 或后续数据,就会触发 Broken pipe。
- 网络中断: 在握手过程中网络波动导致 TCP 连接断开。
5. 总结
RTMP 握手是一个严格的三步交换过程:
- C0+C1 (Client Init)
- S0+S1+S2 (Server Response)
- C2 (Client Ack)
开发 RTMP 客户端时,务必注意:
- 严格遵循 1536 字节的数据包大小。
- 正确回显 S1 中的时间戳和随机数据到 C2。
如果服务器支持复杂握手,需实现 HMAC-SHA256 摘要计算。
处理好异常,一旦握手失败(如收到 RST 包或超时),应立即关闭 Socket 并重连,避免对已关闭的连接进行写操作。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)