RTMP详细分析(Message 消息,Chunk分块)
librtmp分析(发送数据包处理)
librtmp分析(接收数据包处理)
RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应
用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。随
着VR技术的发展,视频直播等领域逐渐活跃起来,RTMP作为业内广泛使用的协议也重新被相关开发者重
视起来。
目录
- 1、介绍:
- 2.1、握手:
- 2.2、握手过程:
- 3.1 、C0和S0格式(简单握手):
- 3.2 、C1和S1格式(简单握手):
- 3.3 、C2和S2格式(简单握手):
- 4、复杂握手:
1、介绍:
RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在
基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之
上的RTMP Connection链接,在Connection链接上会传输一些控制信息,如
SetChunkSize,SetACKWindowSize。其中CreateStream命令会创建一个Stream链接,用于传输具体的
音视频数据和控制这些信息传输的命令信息。RTMP协议传输时会对数据做自己的格式化,这种格式的消
息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送
端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是
Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把
chunk还原成完整的Message,从而实现信息的收发。
2.1、握手:
一个 RTMP 连接以握手开始。RTMP 的握手不同于其他协议˗ RTMP 握手由三个固定
长度的块组成,而不是像其他协议一样的带有报头的可变长度的块。
客户端 (发起连接请求的终端) 和服务器端各自发送相同的三块。
客户端发送的这些块称为C0、 C1 和 C2,服务器端发送的这些块称为 S0、
S1 和 S2。
2.2、握手过程:
本身并没有规定这6个Message的具体传输顺序,但RTMP协议的实现者需要保证这几点:
客户端要等收到S1之后才能发送C2
客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据)
服务端要等到收到C0之后发送S1
服务端必须等到收到C1之后才能发送S2
服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据)
理论上来讲只要满足以上条件,如何安排6个Message的顺序都是可以的,但实际实现中为了在保证握手
的身份验证功能的基础上尽量减少通信的次数,一般的发送顺序是这样的,这一点可以通过wireshark抓推流包进行验证:
握手流程图:
Uninitialized (未初始化):协议的版本号在这个阶段被发送。客户端和服务器都是。
uninitialized (未初始化)状态。之后客户端在数据包 C0 中将协议版本号发出。如果服务器
支持这个版本,它将在回应中发送S0 和 S1。如果不支持,服务器会才去适当的行为进
行响应。在 RTMP 协议中,这个行为就是终止连接。
Version Sent (版本已发送):在未初始化状态之后,客户端和服务器都进入 Version Sent
(版本已发送) 状态。客户端会等待接收数据包 S1 而服务器在等待 C1。一旦拿到期待的包,
客户端会发送数据包 C2 而服务器发送数据包 S2。 (客户端和服务器各自的)状态随即变为
Ack Sent (确认已发送 )。
Ack Sent (确认已发送):客户端和服务器分别等待 S2 和 C2。
Handshake Done (握手结束):客户端和服务器可以开始交换消息了。
3.1 、C0和S0格式(简单握手):
C0 和 S0 包都是一个单一的八位字节,以一个单独的八位整型域进行处理:
版本( 八位):在 C0 中,这一字段指示出客户端要求的 RTMP 版本号。
在 S0 中,这一字段指示出服务器端选择的 RTMP 版本号。版本号基本都是3。
0、1、2 这三个值是由早期其他产品使用的,是废弃值。
4 - 31 被保留为RTMP 协议的未来实现版本使用。
32 - 255 不允许使用 (以区分开 RTMP 和其他常以一个可打印字符开始的文本协议)。
无法识别客户端所请求版本号的服务器应该以版本 3 响应, (收到响应的) 客户端可以选择降低到版本 3,或者放弃握手。
3.2 、C1和S1格式(简单握手):
C1 和 S1 数据包的长度都是 1536 字节,分布如下:
Time (四个字节):这个字段包含一个 timestamp,用于本终端发送的所有后续块的时间
起点。这个值可以是 0。
Zero (四个字节):这个字段必须都是 0。
Random data (1528 个字节):这个字段可以包含任意值。终端需要区分出响应来自它发
起的握手还是对端发起的握手,这个数据应该发送一些足够随机的数。这个不需要对随机数进行加密保护,也不需要动态值。
3.3 、C2和S2格式(简单握手):
C2 和 S2 数据包长度都是 1536 个节,基本就是 S1 和 C1 的副本。分布如下:
Time (四个字节):这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的
timestamp。
Time2 (四个个节):这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。
Random echo (1528 个字节):这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1)
的随机数。
4、复杂握手:
复杂握手主要是增加了更严格的验证。
主要是将简单握手中1528Bytes随机数的部分平均分成两部分,
一部分764Bytes存储public key(公共密钥),另一部分
764Bytes存储digest(密文, 32字节)。
另外, 复杂握手还有一个明显的特征就是: Version部分不为0,
服务器端可根据这个来判断是否简单握手或复杂握手。