rtmp源码分析---握手

/*请依照文字阅读下面源代码*/

握手协议

一个RTMP连接以握手开始。这里的握手和其他协议的握手不一样。这里的握手由三个固定大小的块组成,而不是可变大小的块加上头。
   客户端(发起连接的一方)和服务端各自发送三个相同的块。这些块如果是客户端发送的话记为C0,C1和C2,如果是服务端发送的话记为S0,S1和S2。
1 握手队列
  一个RTMP连接以握手开始,双方分别发送大小固定的三个数据块
   a) 握手开始于客户端发送C0、C1块。服务器收到C0或C1后发送S0和S1。
   b) 当客户端收齐S0和S1后,开始发送C2。当服务器收齐C0和C1后,开始发送S2。
   c) 当客户端和服务器分别收到S2和C2后,握手完成。
2 C0 和 S0消息格式
 C0和S0是单独的一个字节 。
             在这里插入图片描述
 下面是C0和S0包的字段说明。
  版本:8位
   在C0中这个字段表示客户端要求的RTMP版本 。在S0中这个字段表示服务器选择的RTMP版本。本规范所定义的版本是3;0-2是早期产品所用的,已被丢弃;4-31保留在未来使用 ;32-255不允许使用 (为了区分其他以某一字符开始的文本协议)。如果服务无法识别客户端请求的版本,应该返回3 。客户端可以选择减到版本3或选择取消握手。
3 C1 和 S1消息格式
  C1和S1消息有1536字节长,由下列字段组成。
          在这里插入图片描述
  时间:4字节
   本字段包含时间戳。该时间戳应该是发送这个数据块的端点的后续块的时间起始点。可以是0,或其他的任何值。为了同步多个流,端点可能发送其块流的当前值。
  零:4字节
    本字段必须是全零。
  随机数据:1528字节。
    本字段可以包含任何值。因为每个端点必须用自己初始化的握手和对端初始化的握手来区分身份,所以这个数据应有充分的随机性。但是并不需要加密安全的随机值,或者动态值。
4 C2 和 S2 消息格式
 C2和S2消息有1536字节长。只是S1和C1的回复。本消息由下列字段组成。
            在这里插入图片描述
  时间:4字节
    本字段必须包含对等段发送的时间(对C2来说是S1,对S2来说是C1)。
  时间2:4字节
    本字段必须包含先前发送的并被对端读取的包的时间戳。
  随机回复:1528字节
    本字段必须包含对端发送的随机数据字段(对C2来说是S1,对S2来说是C1)。
每个对等端可以用时间和时间2字段中的时间戳来快速地估计带宽和延迟。但这样做可能并不实用。
5、握手示意图
             在这里插入图片描述

源代码

注意: handshake.h里面代码量很大,但是很多代码都是为了处理RTMP的加密版协议的,例如rtmps;因此在这里就不做过多分析了,我们只考虑普通的RTMP协议(rtmp.c中)。

static bool HandShake(RTMP *r, bool FP9HandShake)
{
	int i;
	uint32_t uptime, suptime;
	bool bMatch;
	char type;
	char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
	char serversig[RTMP_SIG_SIZE];

	clientbuf[0] = 0x03;		/* zx C0:上面协议规定,客户端要求的版本是3 */

	uptime = htonl(RTMP_GetTime());
	memcpy(clientsig, &uptime, 4);

	memset(&clientsig[4], 0, 4);	/* zx C1:4字节时间戳+4字节0+1528字节随机数*/

#ifdef _DEBUG
	for (i = 8; i < RTMP_SIG_SIZE; i++)
		clientsig[i] = 0xff;
#else
	for (i = 8; i < RTMP_SIG_SIZE; i++)
		clientsig[i] = (char)(rand() % 256);
#endif

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

	if (ReadN(r, &type, 1) != 1)	/* zx S0:客户端等待接收到的数据也是0x03(RTMP) */
		return false;

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

	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)		/* zx S1:等待读取S1 */
		return false;

	/* decode server response */

	memcpy(&suptime, serversig, 4);
	suptime = ntohl(suptime);			/* zx 网络字节序转化为主机字节序 */

	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;

	if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)		/*  zx S2:等待读取到的S2就是客户端之前发送过去的S1,要进行比对是否相同 */
		eturn false;

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


uint32_t  RTMP_GetTime()
{
#ifdef _DEBUG
  return 0;
#elif defined(_WIN32)
  return timeGetTime();
#else
  struct tms t;
  if (!clk_tck) clk_tck = sysconf(_SC_CLK_TCK);			/* zx 获得当前系统时钟滴答数 */
  return times(&t) * 1000 / clk_tck;					/* zx 得到当前时间 */
#endif
}

猜你喜欢

转载自blog.csdn.net/weixin_37921201/article/details/90047494
今日推荐