RTMP协议学习笔记-握手
2016-03-11 00:00
232 查看
摘要: 从基础开始,一点一点的揭开它的面纱
##握手
####握手
** 首先是从开始地方了解起:
一个 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 消息格式
####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的交互规则和具体格式查看。
##参考链接及资料
https://en.wikipedia.org/w/index.php?title=Real_Time_Messaging_Protocol&gettingStartedReturn=true
rtmp规范1.0
http://wenku.baidu.com/link?url=xOoH3887nJXUCzST1YBQpNvFsuiXLvjnHL-aSWb99YLzun0wNzQ33jBOf_bIsroTlz_PShfEGJEFePQOO1WnPs28eO4ehf_p0f6fO6f5bCu
##握手
####握手
** 首先是从开始地方了解起:
一个 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; }
##参考链接及资料
https://en.wikipedia.org/w/index.php?title=Real_Time_Messaging_Protocol&gettingStartedReturn=true
rtmp规范1.0
http://wenku.baidu.com/link?url=xOoH3887nJXUCzST1YBQpNvFsuiXLvjnHL-aSWb99YLzun0wNzQ33jBOf_bIsroTlz_PShfEGJEFePQOO1WnPs28eO4ehf_p0f6fO6f5bCu
相关文章推荐
- ip地址基础知识
- 如何选择路由协议
- BGP边界网关协议
- VBS基础编程教程 (第1篇)
- VBS基础编程教程 (第3篇)
- 路由器基础精析
- VBS基础编程教程 (第4篇)
- VBS基础编程教程 (第5篇)
- VBS基础编程教程 (第6篇)
- VBS编程教程 (第2篇)
- AJAX初级教程之初识AJAX
- Jquery 基础学习笔记
- PHP学习一(基础)第1/2页
- 《JavaScript DOM 编程艺术》读书笔记之DOM基础
- 精通Javascript系列之Javascript基础篇
- JavaScript 学习笔记之基础中的基础
- jQuery基础知识小结
- JQuery入门基础小实例(1)
- jquery 指南/入门基础
- .NET开发基础:从简单的例子理解泛型 分享