您的位置:首页 > 其它

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 消息格式



**说明: 在 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息