文档章节

RTMP协议学习笔记-握手

Ginter
 Ginter
发布于 2016/03/11 11:24
字数 1452
阅读 137
收藏 3

##握手 ####握手 ** 首先是从开始地方了解起: 一个 RTMP 连接以握手开始。这里的握手和其他协议的握手不一样。这里的握手由三个固定 大小的块组成,而不是可变大小的块加上头。 客户端(发起连接的一方)和服务端各自发送三个相同的块。这些块如果是客户端发送的话 记为 C0,C1 和 C2,如果是服务端发送的话记为 S0,S1 和 S2。 这握手的有什么作用呢?我们继续后面看?** ####握手队列

  • 握手开始于客户端发送 C0,C1 块。
  • 在发送 C2 之前客户端必须等待接收 S1 。
  • 在发送任何数据之前客户端必须等待接收 S2。
  • 服务端在发送 S0 和 S1 之前必须等待接收 C0,也可以等待接收 C1。
  • 服务端在发送 S2 之前必须等待接收 C1。
  • 服务端在发送任何数据之前必须等待接收 C2。

输入图片说明 上图中的C0代表客户端要求RTMP使用的版本号! 握手开始于客户端,客户端和服务器在发送C2或S2之前必须接受到对应的C1和S1 ####C0 和 S0 消息格式

输入图片说明

**说明: 在 S0 中这个字段表示服务器选择的 RTMP 版本。rtmp1.0规范所定义的版本是 3;0-2 是早期产品所用的,已被丢弃;4-31保留在未来使用;32-255 不允许使用(为了区分其他以某一字符开始的文本协议)。如果服务无法识别客户端请求的版本,应该返回 3 。客户端可以选择减到版本 3 或选择取消握手**。

_接下来我们了解C1和S1_

####C1 和 S1 消息格式 C1 和 S1 消息有 1536 字节长,由下列字段组成。

输入图片说明

  • 时间:4 字节 本字段包含时间戳。该时间戳应该是发送这个数据块的端点的后续块的时间起始点。可以是 0,或其他的任何值。为了同步多个流,端点可能发送其块流的当前值。
  • 零:4 字节 ** 本字段必须是全零。**
  • 随机数据:1528 字节。 本字段可以包含任何值。 因为每个端点必须用自己初始化的握手和对端初始化的握 手来区分身份,所以这个数据应有充分的随机性。但是并不需要加密安全的随机值,或者动态值 ####C2 和 S2 消息格式 C2 和 S2 消息有 1536 字节长。只是 S1 和 C1 的回复。本消息由下列字段组成。

输入图片说明

  • 时间:4 字节 本字段必须包含对等段发送的时间(对 C2 来说是 S1,对 S2 来说是 C1)。
  • 时间 2:4 字节 本字段必须包含先前发送的并被对端读取的包的时间戳。
  • 随机回复:1528 字节 本字段必须包含对端发送的随机数据字段(对 C2 来说是 S1,对 S2 来说是 C1) 。 每个对等端可以用时间和时间 2 字段中的时间戳来快速地估计带宽和延迟。 但这样做可 能并不实用。

##源码分析 源码选择的是ffmpeg中的librtmp库,网上相关的资料也比较多。 现在分析librtmp中HandShake函数(不加密),因为也有加密的函数,今天主要了解基本握手功能。 图中注释(蓝色字体)是对rtmp源码中握手的分析,请参照上面的C0,S0,C1,S1,C2,S2的交互规则和具体格式查看。

   static int  
HandShake(RTMP *r, int FP9HandShake)
{
  int i;
  uint32_t uptime, suptime;
  int bMatch;
  char type;

  //"RTMP_SIG_SIZE 是定义的C1,C2的长度大小1536字节"
  //"此处让clientsig 指向了clientbuf数组的下表1开始的位置即C1的头"

  char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
  char serversig[RTMP_SIG_SIZE];

  //  "clientbuf的长度刚好是1536+1,此处第一个字节因为是0x03" 
  //  "这是rtmp规范中定义的客户端要求使用的rtmp版本,即C0" 

  clientbuf[0] = 0x03;		/* not encrypted */

  //"从源码中看RTMP_GetTime()是用sysconf(_SC_CLK_TCK)函数获取系统clock的再处理(linux下)"
  //  "之后转化网络字节序作为C1的时间戳"

  uptime = htonl(RTMP_GetTime()); 

  //"void *memcpy(void *dest, const void *src, int n)"
  //"将时间戳填到clientsig前四个字节中 (C1)"

  memcpy(clientsig, &uptime, 4);

  //"将clientsig中4~7字节置为0 (C1)"

  memset(&clientsig[4], 0, 4);

#ifdef _DEBUG
  for (i = 8; i < RTMP_SIG_SIZE; i++)
    clientsig[i] = 0xff;
#else

  //"给clientsig中后面 1528 个字节填上随机数(C1)"

  for (i = 8; i < RTMP_SIG_SIZE; i++)
    clientsig[i] = (char)(rand() % 256);
#endif

  //"发送clientbuf共1537字节(C0+C1)到服务器"

  if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1))
    return FALSE;

  //"从服务器接受了一个字节即S0,并检查版本"

  if (ReadN(r, &type, 1) != 1)	/* 0x03 or 0x06 */
    return FALSE;

  RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer   : %02X", __FUNCTION__, type);

  //"若S0没问题的话,继续接受服务器的S1"

  if (type != clientbuf[0])
    RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d",
	__FUNCTION__, clientbuf[0], type);

  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
    return FALSE;

  /* decode server response */

  //"以下一段是保存服务器的S1然后把服务器的S1作为客户端的C2发送给服务器"
  //"int memcmp(const void *s1, const void *s2, size_t n); 返回0即s1=s2"

  memcpy(&suptime, serversig, 4);
  suptime = ntohl(suptime);

  RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime);
  RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__,
      serversig[4], serversig[5], serversig[6], serversig[7]);

  /* 2nd part of handshake */
  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
    return FALSE;

  //"接受服务器的S2"

  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
    return FALSE;

  //"比较服务器的S2是否跟客户端C1相同,若是,则握手成功"

  bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
  if (!bMatch)
    {
      RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);
    }
  return TRUE;
}

##参考链接及资料

  1. https://en.wikipedia.org/w/index.php?title=Real_Time_Messaging_Protocol&gettingStartedReturn=true
  2. rtmp规范1.0
  3. http://wenku.baidu.com/link?url=xOoH3887nJXUCzST1YBQpNvFsuiXLvjnHL-aSWb99YLzun0wNzQ33jBOf_bIsroTlz_PShfEGJEFePQOO1WnPs28eO4ehf_p0f6fO6f5bCu

© 著作权归作者所有

共有 人打赏支持
Ginter
粉丝 1
博文 21
码字总数 11752
作品 0
成都
程序员
私信 提问
ios视频直播(二)- RTMP协议学习总结

一。什么是rtmp协议 RTMP协议就是Real Time Messaging Protocol,实时消息传输协议, 是Adobe公司为Flash播放器和服务器之间音、视频及数据传输开发的实时消息传送协议。协议中,视频必须是H26...

鹿微微鹿
2016/06/25
113
3
函数rtmp_open()

FFMPEG版本为3.2 release。 libavformat/rtmpproto.c 调用关系: 即 Open RTMP connection and verify that the stream can be played. 该函数主要包括四个功能: 1.解析url 2.打开连接 3. 三......

andrew810810
2016/12/07
15
0
用一首歌曲来谱写RTMP协议分析

  *本文原创作者:星空111,本文属FreeBuf原创奖励计划,未经许可禁止转载   为了让我这篇技术稿更加有情趣,打算用一首歌曲的结构来命名我整篇文章的脉络。   一、前序   最近在重温...

FreeBuf
2018/08/20
0
0
[总结]RTMP流媒体技术零基础学习方法

本文主要总结一些我在学习RTMP流媒体技术过程中积累的经验。也为后来学习RTMP流媒体技术的人们一个参考。本文力图从简到难,循序渐进的介绍RTMP流媒体技术的方方面面,先从应用说起,逐步深化...

leixiaohua1020
2013/11/18
0
0
流媒体技术学习笔记之(一)nginx+nginx-rtmp-module+ffmpeg搭建流媒体服务器

参照网址: 【1】http://blog.csdn.net/redstarofsleep/article/details/45092147 【2】HLS介绍:http://www.cnblogs.com/haibindev/archive/2013/01/30/2880764.html 上面这两个流的地址分别......

tinywan1227
2016/09/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

equals()的重写规则

自反性。对于任何非null的引用值x,x.equals(x)应返回true。 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。 传递性。对于任何非null的引用...

无精疯
12分钟前
0
0
Go基础系列:双层channel用法示例

双层通道的解释见Go的双层通道 以下是一个双层通道的使用示例。注意下面的示例中使用了"信号通道"(Signal channel),但这里的信号通道是多余的,仅仅只是为了介绍。 信号通道不用来传递数据,...

echojson
20分钟前
0
0
PHP文件上传error的错误类型

PHP文件上传error的错误类型 - $_FILES['file']['error'] 有以下几种类型 1、UPLOAD_ERR_OK 其值为 0,没有错误发生,文件上传成功。 2、UPLOAD_ERR_INI_SIZE 其值为 1,上传的文件超过了 ph......

小良下山化了个缘
47分钟前
2
0
分布式项目(四)Mapping Server 数据映射

上回说道CoAp client和server的实现,数据也安装定义的格式发送到了kafka中,接下来就是Mapping server的实现,物理设备数据映射到抽象设备上,并赋予数据业务含义。 iot-mapping 构建iot-m...

lelinked
56分钟前
3
0
使用data pump驱动的外部表移动数据

使用data pump驱动的外部表移动数据 比如我们有一个报表的数据,准备从一个数据库A中移动到另一个数据库B中,如何实现? 这个问题,我们使用带data pump驱动的外部表方式,很容易实现,具体方法如下...

突突突酱
59分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部