1 协议研究概述,转载请注明出处

时间:2019-10-08 11:59来源:美高梅手机游戏网站
AMF(Action MessageFormat)是Flash与服务端通信的一种常见的二进制编码模式,其传输效率高,可以在HTTP层面上传输。现在很多FlashWebGame都采用这样的消息格式。AMF协议是基于Http协议的.它的内

AMF(Action Message Format)是Flash与服务端通信的一种常见的二进制编码模式,其传输效率高,可以在HTTP层面上传输。现在很多Flash WebGame都采用这样的消息格式。AMF协议是基于Http协议的.它的内容处理过程大致是这样:1.从客户端获取Http请求流.2.对流进行解串行化(Deserialize),得到服务器端程序能够识别的数据,并建立一个响应消息3.Debug开始4.对流进行各种处理得到返回值5.对响应流进行串行化6.发送Http响应给客户端

2018 年 12 月,接到了一个调查原有直播系统播放端与编码端时差对不上的任务,于是首先开始学习 RTMP 协议,之后阅读 nginx_rtmp_module 代码,调试程序,调查原因,经过一个多月的努力,幸不辱命,终于查清并解决了该问题,成功为公司的实时直播系统优化掉 10 多毫秒的延时;于是公司安排我讲解 RTMP 协议 和 nginx_rtmp_module 代码结构与流程,遂生成了此篇文章。虽然很多内容来自于网络,但我详细整理和补充了其中的内容,修正了很多网上的错误,望能帮助到各位做流媒体开发的朋友,也请大家能尊重我的辛苦付出,转载请注明出处,谢谢!

看了一周Flex/Flash的相关东东,发现总在与RTMP这个字眼打交道,今天终于下决心想深入了解这玩意儿,找到一篇不错的文章,转载与此,原文地址:

1、下载pyamfhttps://pypi.python.org/pypi/PyAMF2、安装pyamf下载完成之后解压出来,打开DOS命令进入pyamf目录,执行下列命令,安装pyamf框架python setup.py install --disable-ext

  • 简介
  • 握手包
  • 块格式
  • 协议控制消息
  • AMF 消息

正文开始:

首先使用charles抓包工具,抓取AMF包,得到请求/应答的AMF数据,对AMF格式的请求和响应的数据进行分析。解密的POST DATA如下图所示:

RTMP 协议研究

美高梅手机游戏网站 1

  • 握手及通用命令
  • Publish 过程分析
  • Play 过程分析

 

构建flex.messaging.messages.RemotingMessage信息

RTMP 是 Real Time Messaging Protocol的首字母缩写。是Adobe Systems 公司为 Flash 播放器和服务器之间音频、视频和数据传输 开发的开放协议。该协议基于 TCP,是一个协议族,包括 RTMP 基本协议及 RTMPT/RTMPS/RTMPE等多种变种。

1 协议研究概述    协议设计和分析一直都是在工作遇到,正好在这里总结一下,说到协议,在这个网络的时代,没有人可以离开它了。他存在我们生活中的任何角落,只不过我们平时,并没有注意到它的存在,可以这么说如果没有协议,我们生活和日常的工作生产都不能进行。如果仔细想想你生活中用到的所有东西,协议已经包含其中。那到底什么是协议呢?说的简单一点就是双方达成的共识,以便更好的交流,理论上协议是什么呢?如果学过《信号与系统》的人都知道有个简单的道理,就是信息在经过一个管道的符号集,到另一个符号集时信息不会丢失。

msg= messaging.RemotiongMessage(messageId=str(uuid.uuid1.upper(), clometOd=None, operation='playeSource', destination='metaDataService', timeTolive=0, timestamp=0 )msg.body=['xxx', 'aaaaa', 'ccccc','ddddd', '', 'eeeee', '']msg.headers['DSEndpoint']='my-amf'msg.headers['DSId']=str(uuid.uuid1.upper()......
  1. RTMP 工作在 TCP 之上,默认使用端口 1935;
  2. RTMPE 在 RTMP 的基础上增加了加密功能;
  3. RTMPT 封装在 HTTP 请求之上,可穿透防火墙;
  4. RTMPS 类似 RTMPT,增加了 TLS/SSL 的安全功能;

    任何复杂的事物都有个最简单的本质,网络上的协议也是这样,有个最基本的本质。除去上下层的概念,协议就只剩下通信双方实体的规则。

得到的Response如下图:

RTMP 协议中数据都是大端

美高梅手机游戏网站,   一般的协议都包含最基本的协议头,不管是物理层、链路层、还是网络层,这个头就构成了协议的本质东西。通常协议头要包含以下最基本的三项信息:

美高梅手机游戏网站 2

美高梅手机游戏网站 3image.png

双方实体的唯一标示,用来标示通信双方的实体。
类型描述或者是净核描述,标志净核的内容。
协议净核的长度,用来在萃取净核的内容应用。
   其中,前两项是必须要有的,没有他们,通信双方的交互根本得不到保证,第三项在不太灵活的通信中可以去掉,而有第二项的类型推出。

import urllib2import uuidimport pyamffrom pyamf import remotingfrom pyamf.flex import messaging# 构造flex.messaging.messages.RemotingMessage消息msg= messaging.RemotiongMessage(messageId=str(uuid.uuid1.upper(), clometOd=None, operation='playeSource', destination='metaDataService', timeTolive=0, timestamp=0 )msg.body = ['xxx', 'aaaaa', 'ccccc','ddddd', '', 'eeeee', '']msg.headers['DSEndpoint'] = 'my-amf'msg.headers['DSId'] = str(uuid.uuid1.upper()# 按AMF协议编码req = remoting.Request('null', body=env = remoting.Envelope(amfVersion=pyamf.AMF3)env.bodies = []data = bytes(remoting.encode.read# 提交请求url = 'http://xxxx.xx.xx/amf'req = urllib2.Request(url,data,headers={'Content-Type':'application/x-amf'})# 解析返回数据oepner = urllib2.build_opener()# 解码AMF协议返回的数据resp = remoting.decode(opener.open.readprint resp.bodies[0][1].body.body['xxxx']......
  • C0 / S0
  • C1 / S1
  • C2 / S2
  • 简单握手 / 复杂握手

    协议的丰富性,有净核的多样性体现。

C0 和 S0 包由一个字节组成(目前的值应该为 3),下面是 C0/S0 包内的字段:

   协议头除了以上的三项,还可以增加更多的信息(比如控制信息、时间信息等),取决于具体的应用。找到这些基本的东西,再去看协议的时候,能够更好的抓住协议的主体进行分析和设计了。

美高梅手机游戏网站 4image.png

  

在 C0 包内,这个字段代表客户端请求的 RTMP 版本号。在 S0 包内,这个字段代表服务端选择的 RTMP 版本号。此文档使用的版本是 3。版本 0-2 用在早期的产品中,现在已经被弃用;版本 4-31 被预留用于后续产品;版本 32-255(为了区分 RTMP 协议和文本协议,文本协议通常以可打印字符开始)不允许使用。如果服务器无法识别客户端的版本号,应该回复版本 3。客户端可以选择降低到版本 3,或者中止握手过程。

2 RTMP 协议概述
   RTMP 协议是被 Flash 用于对象、视频、音频的传输。该协议建立在 TCP 协议或者轮询 HTTP 协议之上, RTMP 协议就像一个用来装数据包的容器,这些数据可以是 AMF 格式的数据,也可以是 FLV 中的视 / 音频数据。一个单一的连接可以通过不同的通道传输多路网络流,这些通道中的包都是按照固定大小的包传输的 .

C1 和 S1 包长度为 1536 字节,包含以下字段:包内的字段:

  

美高梅手机游戏网站 5image.png

3 RTMP 协议部分

time: 毫秒值的时间戳,这个值可以是 0,或者一些任意值。用于本终端发送的所有后续块的时间起点。zero: 必须全 0Random data: 本字段可以包含任意数据。由于握手的双方需要区分另一端,此字段填充的数据必须足够随机(以防止与其他握手端混淆)。不过没必要为此使用加密数据或动态数据。服务端必须接收到 C0 消息,才能发送 S0 和 S1 消息。

3.1 协议头
struct RTMP_HEAD

客户端必须接收到 S1 消息,然后发送 C2 消息。客户端必须接收到 S2 消息,然后发送其他数据。服务端必须接收到 C1 消息,然后发送 S2 消息。服务端必须接收到 C2 消息,然后发送其他数据。

{

美高梅手机游戏网站 6image.png

      char cChannelid : 6;// 第一个字节的后 6 位

time1: 这个字段必须是对端发送过来的时间戳(C1 或 S1 内的)。time2: 这个字段值为本机接收到对端发送过来的握手包的时刻。random echo: 这个字段必须是对端发送过来的随机数据。握手的双方可以使用 time1 和 time2 字段来估算网络连接的带宽和/或延迟,但是不一定有用。

      char cCheadsize ; // 第一个字节的头两位

前面描述的握手协议是标准的 Adobe RTMP 文档中描述的内容,我们称为简单握手。在 Flash 10.1 之后,Adobe 公司改了握手,简单握手不能用了,但是 Adobe 公司并没有修正文档。握手步骤没有变,但内容完全不一样,数据是加密的。它的步骤是由三个固定大小的块组成,而不是可变大小的块加上头。握手开始于客户端发送 C0 + C1 块。在发送 C2 之前客户端必须等待接收 S1。在发送任何数据之前客户端必须等待接收 S2。服务端在发送 S0 和 S1 之前必须等待接收 C0,也可以等待接收C1。服务端在发送 S2 之前必须等待接收 C1。服务端在发送任何数据之前必须等待接收 C2。C1 / S1 和 C2 / S2 的 1536 字节是:

      char cTimer[3];  // 三个字节表示的时间信息

  • 4 字节的当前时间:(u_int32_t)time
  • 4 字节的程序版本:C1 一般是 0x80000702,S1 是 0x04050001
  • 764 字节的 KeyBlock 或者 DigestBlock
  • 764 字节的 KeyBlock 或者 DigestBlock在不同的包里,后两个顺序可能会颠倒子结构 KeyBlock 定义:
  • 760 bytes: 包含 128 bytes 的 key 的数据。
  • key_offset: 4 bytes,最后 4 字节定义了 key的 offset(相对于 KeyBlock 开头而言)子结构 DigestBlock 定义:
  • digest_offset: 4bytes,开头 4 字节定义了 digest 的 offset(相对于 DigestBlock 的第 5 字节而言,offset=3 表示 digestBlock[7~38] 为 digest
  • 760bytes: 包含 32 bytes 的 digest 的数据。

      char cLength[3]; // 三个字节表示的长度

握手之后,连接复用一个或多个块流。创建的每个块都有一个唯一 ID 对其进行关联,这个 ID 叫做 chunk stream ID ,每一类 csid 都对应一种功能。传递时,每个块必须被完全发送才可以发送下一块。在接收端,这些块被根据块流 ID 被组装成消息。组块允许更高层协议中的大消息分解成较小的消息,例如防止大的、低优先级的消息阻塞较小但高优先级的消息,如:音频或控制。块大小是可配置的。

      char cDatatype; // 数据类型

美高梅手机游戏网站 7image.png

      char sStreamid[4]; // 流标识

Basic Header (基本头,1 到 3 个字节):这个字段对块流 ID 和块类型进行编码。长度完全取决于块流 ID,因为块流 ID 是一个可变长度的字段。Message Header (消息头,0,3,7,或者 11 个字节):这一字段对正在发送的消息 (不管是整个消息,还是只是一小部分) 的信息进行编码。这一字段的长度可以使用块头中定义的块类型进行决定。Extended Timestamp (扩展 timestamp,0 或 4 字节):这一字段是否出现取决于块消息头中的 timestamp 或者 timestamp delta 字段。它表示的是时间戳的增量,需要与 timestamp 或者 timestamp delta 相加获得总时间戳。Chunk Data :当前块的有效负载,相当于定义的最大块大小。

};

RTMP 协议最多支持 65597 个流,CS ID 范围为: 3 ~ 65599。ID 0、1、2 被保留。0 值表示二字节形式,并且 ID 范围 64 ~ 319 (第二个字节 + 64)。1 值表示三字节形式,并且 ID 范围为 64 ~ 65599 * 256 + 第二个字节 + 64)。3 ~ 63 范围内的值表示整个流 ID。带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。块基本头中的 0 ~ 5 位 代表块流 ID,块流 ID 2 ~ 63 可以编进这一字段的一字节版本中。

这里有三个最基本的元素(唯一标示 )、(类型 )和(净核的长度 )分别是: cChannelid 、 cDatatype 和 cLength 。

美高梅手机游戏网站 8image.png

 

Basic Header 为 1 字节的情况

美高梅手机游戏网站 9image.png

3.2 数据类型
数据类型 决定了协议上层可以做的具体的事情,和使用协议的人必须遵循的规则。

Basic Header 为 2 字节的情况 (第一个字节后 6 位为 000000)

美高梅手机游戏网站 10image.png

同时数据类型 说明了净核 的基本内容。

编辑:美高梅手机游戏网站 本文来源:1 协议研究概述,转载请注明出处

关键词: