RTMP协议(二)握手流程

若要建立一个有效的 RTMPConnection链接,首先需要“握手”:客户端要向服务器按序发送C0,C1,C2三个chunk,服务器向客户端按序发送S0,S1,S2三个chunk,然后才能进行有效的信息传输。RTMP 协议本身并没有规定这6个信息的具体传输顺序,但 RTMP 协议的实现者需要保证这几点如下:

  • 对于 RTMP 客户端
    • 握手以客户端发送C0C1开始
    • 客户端必须接收S1之后才能发送C2
    • 客户端必须接收S2之后才能发送其他数据
  • 对于 RTMP 服务端
    • 服务端必须接收到C0才能发送S0S1,也可以等待接收到C1再发送S0S1
    • 服务端必须接收C1才能发送S2
    • 服务端必须接收C2才能发送其他数据

握手示意图

+-------------+                           +-------------+
|    Client   |       TCP/IP Network      |    Server   |
+-------------+            |              +-------------+
      |                    |                     |
Uninitialized              |               Uninitialized
      |          C0        |                     |
      |------------------->|         C0          |
      |                    |-------------------->|
      |          C1        |                     |
      |------------------->|         S0          |
      |                    |<--------------------|
      |                    |         S1          |
 Version Sent              |<--------------------|
      |          S0        |                     |
      |<-------------------|                     |
      |          S1        |                     |
      |<-------------------|                Version Sent
      |                    |         C1          |
      |                    |-------------------->|
      |          C2        |                     |
      |------------------->|         S2          |
      |                    |<--------------------|
   AckSSent                |                  Ack Sent
      |          S2        |                     |
      |<-------------------|                     |
      |                    |         C2          |
      |                    |-------------------->|
 Handshake Done            |               Handshake Done
      |                    |                     |
          Pictorial Representation Of Handshake

握手状态

  • Uninitialized (未初始化):协议的版本在这个状态中被发送。客户端在数据包C0中将协议版本发出,如果服务端支持这个版本,将在回应中发送S0S1,并进入Version Sent状态。如果不支持,服务端会终止连接。
  • Version Sent (版本已发送):客户端和服务端分别等待接收S1C1。接收完成后,将会进入Ack Sent状态。
  • Ack Sent (确认已发送):客户端和服务端分别等待接收S2C2。接收完成后,进入Handshake Done状态。
  • Handshake Done (握手完成):客户端和服务端可以开始交换消息了。

握手数据格式

在 Flash10.1 之后,Adobe 对 RTMP 握手进行了一轮修改,握手的步骤和上文记述的没有不同,而是对握手的数据进行了修改,采用了加密数据。因此,现在存在两种握手数据,一般通过C1[4:8]是否为0来区分。我们将使用0值称为简单握手(simple handshake),将非0值的称为复杂握手(complex handshake)。

简单握手 - simple handshake

C0 和 S0(1 byte)

    +-+-+-+-+-+-+-+-+-+-+-+
    |  version (1 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+-+
  • version(1 byte):版本号,表示客户端请求的 RTMP 版本号或服务端支持的 RTMP 版本号。当前使用的版本是3

C1 和 S1(1536 bytes)

    +-+-+-+-+-+-+-+-+-+-+
    |   time (4 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+
    |   zero (4 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+
    |   random bytes    |
    +-+-+-+-+-+-+-+-+-+-+
    |   random bytes    |
    |      (cont)       |
    |       ....        |
    +-+-+-+-+-+-+-+-+-+-+
  • time(4 bytes):本字段包含一个发送时间戳(取值可以为零或其他任意值)。客户端应该使用此字段来标识所有流块的时间戳。为了同步多个块流,客户端可能希望多个块流使用相同的时间戳。
  • zero(4 bytes):本字段必须全为零。
  • random (1528 bytes):本字段可以包含任何值。由于握手的双方需要区分另一端,此字段填充的数据必须足够随机(以防止与其他握手端混淆)。不过没有必要为此使用加密数据或动态数据。

C2 和 S2(1536 bytes)

    +-+-+-+-+-+-+-+-+-+-+
    |   time (4 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+
    |  time2 (4 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+
    |   random bytes    |
    +-+-+-+-+-+-+-+-+-+-+
    |   random bytes    |
    |      (cont)       |
    |       ....        |
    +-+-+-+-+-+-+-+-+-+-+
  • time(4 bytes):本字段表示对端发送的时间戳(对C2来说是S1 ,对S2来说是C1)。
  • time2(4 bytes):本字段表示接收对端发送过来的握手包的时间戳。
  • random(1528 bytes):本字段包含对端发送过来的随机数据(对C2来说是S1,对S2来说是C1)。

握手的双方可以使用``timetime2` 字段来估算网络连接的带宽和或延迟,但是不一定有用。

复杂握手 - complex handshake

C0 和 S0(1 byte)

    +-+-+-+-+-+-+-+-+-+-+-+
    |  version (1 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+-+
  • version(1 byte):版本号,表示客户端请求的 RTMP 版本号或服务端支持的 RTMP 版本号。当前使用的版本是3

C1 和 S1(1536 bytes)

    +-+-+-+-+-+-+-+-+-+-+
    |   time (4 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+
    | version (4 bytes) |
    +-+-+-+-+-+-+-+-+-+-+
    |   key (764 bytes) |
    +-+-+-+-+-+-+-+-+-+-+
    | digest (764 bytes)|
    +-+-+-+-+-+-+-+-+-+-+
  • time(4 bytes):发送的时间戳
  • version (4 bytes):版本号
    • 客户端的C1一般是0x80000702
    • 服务端的S1一般是0x040500010x0d0e0a0d(livego中数值)
  • key (764 bytes):结构如下
    • random-data: (offset) bytes
    • key-data: 128 bytes
    • random-data: (764 - offset - 128 - 4) bytes
    • offset: 4 bytes
  • digest (764 bytes):结构如下
    • offset: 4 bytes
    • random-data: (offset) bytes
    • digest-data: 32 bytes
    • random-data: (764 - 4 - offset - 32) bytes

在不同的包里,keydiest顺序可能会颠倒,比如nginx-rtmp

C2 和 S2(1536 bytes)

    +-+-+-+-+-+-+-+-+-+-+-+-+-+
    | random-data (1504 bytes)|
    +-+-+-+-+-+-+-+-+-+-+-+-+-+
    | digest-data (32 bytes)  |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+
  • random-datadigest-data都应来自对应的数据(对C2来说是S1,对S2来说是C1

digest的计算方式较为复杂,后续再补全

握手流程设计的理解

  • C0S0是版本数据包
  • C1S1是带有验证数据的包
  • S2C2是验证C1S1的数据包

实现选用的流程

为了方便开发,在实现上,我们选用以下握手流程,这样服务端可以连续发送S0,S1S2

  1. Client--> Server : 发送一个创建流的请求(C0C1)
  2. Server--> Client : 返回一个流的索引号( S0S1S2)。
  3. Client--> Server : 开始发送 (C2)
  4. Client--> Server : 发送音视频数据(这些包用流的索引号来唯一标识)

你可能感兴趣的:(RTMP协议(二)握手流程)