当前位置: 首页 > news >正文

RTMP协议解析[一]

文章目录

  • RTMP协议解析[一]
  • RTMP握手
    • 简单握手包结构
    • 复杂握手的包结构
    • RTMP握手的时序图
    • 实现

RTMP协议解析[一]

本专栏重点负责介绍RTMP协议的理论部分, 跳过定义,协议与其他协议的优缺点对比,协议的拓展与改进,协议的历史发展等其他废话

RTMP协议的工作流程大致如下:

  1. 客户端与服务器建立TCP连接。
  2. 双方通过握手过程确认协议版本及交换随机数等信息。
  3. 客户端发送连接命令(connect)到服务器。
  4. 服务器响应连接命令,返回连接结果。
  5. 客户端与服务器建立流(stream)进行音视频数据传输。
  6. 在传输过程中,双方可以发送控制命令,如播放(play)、暂停(pause)等。
  7. 当连接关闭时,双方结束消息传输并断开连接。

本专栏重点介绍三个部分

  • RTMP的握手

  • RTMP消息

  • RTMP推流

RTMP握手

握手分为3个阶段

  • C0和S0:客户端和服务器分别发送一个字节的版本号(C0和S0),表示双方的RTMP协议版本。通常情况下,这个版本号是0x03
  • C1和S1:接下来,客户端发送一个2048字节的数据包(C1),其中包括4字节的时间戳、4字节的零填充数据和随机填充的剩余字节。服务器接收到C1后,也会发送一个类似的数据包(S1),包括4字节的时间戳、4字节的客户端时间戳回显(即C1中的时间戳)和随机填充的剩余字节。
  • C2和S2:最后,客户端和服务器互相发送确认数据包(C2和S2),包括4字节的对方发送的时间戳(服务器发给客户端的是S1中的时间戳,客户端发给服务器的是C1中的时间戳),以及4字节的对方接收到的时间戳(服务器发给客户端的是S1中的客户端时间戳回显,客户端发给服务器的是C1中的时间戳)和随机填充的剩余字节。

当然,协议规定了在握手的过程中存在时序的限制:

  • 客户端通过发送 C0 和 C1 消息来启动握手过程

  • 客户端必须接收到 S1 消息,然后发送 C2 消息

  • 客户端必须接收到 S2 消息,然后发送其他数据

  • 服务端必须接收到 C0 或者 C1 消息,然后发送 S0 和 S1 消息

  • 服务端必须接收到 C2 消息,然后发送其他数据

在具体的开始讲解RTMP握手包的包格式的时候,还要区分出简单握手和复杂握手,这两种握手方式的包格式有所差异

简单握手包结构

C0和S0:简单握手的C0和S0包仅一个字节,用来表示rtmp协议的版本号,主流的为3

C1和S1:包长1536个字节,结构如下图

在这里插入图片描述

time字段:时间戳,取值可以为零或其他任意值,用于本终端发送的所有后续块的时间起点

zero字段:必须为0

random字段:可以随意填充,但是为了彼此区分,因此要足够随机,这个不需要对随机数进行加密保护,也不需要动态值。

C2S2:包长1536个字节,结构如下图

在这里插入图片描述

time字段:这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的 timestamp

time2字段:这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp

radom echo字段:这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1) 的随机数。两端都可以一起使用 time 和 time2 字段再加当前 timestamp 以快速估算带宽和/或者连接延迟,但这不太可能是有多大用处。

复杂握手的包结构

在复杂握手中,C0和S0的包结构并没有变化,但是后续的包全都发生了变化,具体变化如下:

C1和S1: 包长度为 1536 字节,除了4字节的时间戳和4字节版本号外,还有764字节的key和764字节的digest,具体结构有两种,如下:

在这里插入图片描述

time字段:同简单握手

version字段:固定取值,客户端为0x0C, 0x00, 0x0D, 0x0E。服务端为0x0D, 0x0E, 0x0A, 0x0D

digest:密文

key:密钥

密钥

在这里插入图片描述

random-data:offset字节的随机数

key-data字段:128字节的密钥

random-data字段:764-offset-128-4个字节的随机数

offset字段:4个字节,用来表示密钥的位置

密文

在这里插入图片描述

offset字段: 4 bytes,digest的位置信息,offset=offset[0]+offset[1]+offset[2]+offset[3] random-data: (offset) bytes,随机数据

digest-data字段: 32 bytes,计算出来的摘要

random-data字段: (764 - 4 - offset - 32) bytes,随机数据

digest的计算方法:digest-data左边部分+digest-data右边部分的内容,以固定的key,做hmac-sha256计算得到digest。

特别的,服务端和客户端的密钥都是公开的:

    static const uint8_t rtmp_player_key[] = {'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ','F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE};static const uint8_t rtmp_server_key[] = {'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ','F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ','S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE};

server 端在收到 C1 后,需要依次尝试 key 和 digest 的两种相对顺序进行解析,得到 key-data 和 digest-data。
key-data 没有用处,digest-data 需要按照 client 生成 C1 digest 的方法,也生成一个进行比较,如果比较失败,则尝试使用 simple handshake

C2S2:包长度为 1536 字节。结构如下:

在这里插入图片描述

random-data字段 (1504 bytes):随机数据

digest-data (32 bytes):random-data的摘要

digest-data计算方法:

​ S2:先通过C1的digest,计算出key,再用这个key计算random-data的digest。

​ C2:先通过S1的digest,计算出key,再用这个key计算random-data的digest。

RTMP握手的时序图

注意握手过程中存在包的时序限制,例如服务端只有收到C0或C1才能发S0S1

协议理想中的握手时序图:

在这里插入图片描述

实际的握手情况:

在这里插入图片描述

因为要收到C0或者C1才能发S0和S1,并且S2的发送并没有限制,因此一般都是一起发送

实现

后续会有专栏专门用C++对Rtmp协议进行实现

  • C1S1的第5~8字节全0,则表示使用简单握手
  • 简单握手不需要验证数据,只需要按照协议按顺序发送和接受消息即可
  • 复杂握手的C1S1,发送发只需要选择其中一种,而接受方先验证其中的一种结构,失败的话,再验证另一种结构即可
  • digest验证失败,则使用简单握手模式
http://www.xdnf.cn/news/572509.html

相关文章:

  • lcd气压表研发方案芯片——用于胎压检测
  • Ubuntu 20.04安装及配置docker
  • 进程调度算法深度剖析:FCFS、SJF、RR、优先级及多级反馈队列全解
  • 算法第25天 | 491. 非递减子序列、46. 全排列、47. 全排列 II
  • Java 实现二进制与十进制之间的互相转换
  • 校平机的原理、应用及发展趋势
  • Vue3学习(Vue3.3新特性——defineModel宏详解)
  • OpenCv高阶(十六)——Fisherface人脸识别
  • MySQL 索引的增删改查
  • Docusaurus Umami
  • 算法优选系列(9.BFS 解决拓扑排序)
  • GStreamer (四)交叉编译
  • 华为eNSP无线AC/AP组网实战
  • 基于大模型的闭合性尺桡骨干骨折全方位诊疗研究报告
  • 现代计算机图形学Games101入门笔记(二十)
  • V少JS基础班之第五弹
  • ElasticSearch导读
  • 【网络安全】日志采集、监控任务守护进程详细教程(附实战案例)
  • 打卡31天
  • Python学习Day1:安装
  • 谷歌2025年I/O开发者大会热点总结
  • shell脚本总结3
  • 【LLMs篇】12:Qwen3 技术报告翻译
  • 人工智能路径:技术演进下的职业发展导航
  • 20个关于Java编程语言的常见问题
  • 从微积分到集合论(1630-1910)(历史简介)——第2章——牛顿(Newton)和莱布尼兹(Neibniz)以及莱布尼兹传统(H.J.M.Bos)
  • 2025年人工智能新应用与新技术全景解析
  • Qt+线段拖曳示例代码
  • 【UE5】环形菜单教程
  • 现代计算机图形学Games101入门笔记(十九)