rtmp source code analysis---handshake

/*Please read the source code below according to the text*/

Handshake agreement

An RTMP connection starts with a handshake. The handshake here is different from the handshake of other protocols. The handshake here consists of three fixed-size blocks instead of variable-size blocks plus headers.
   The client (the party that initiated the connection) and the server each send three identical blocks. These blocks are marked as C0, C1 and C2 if they are sent by the client, and S0, S1 and S2 if they are sent by the server.
1 Handshake queue
  An RTMP connection starts with a handshake, and both parties send three data blocks of fixed size.
   a) The handshake starts when the client sends C0 and C1 blocks. The server sends S0 and S1 after receiving C0 or C1.
   b) When the client receives all S0 and S1, it starts to send C2. When the server receives C0 and C1, it starts to send S2.
   c) When the client and server receive S2 and C2 respectively, the handshake is completed.
2 C0 and S0 message format
 C0 and S0 are a single byte.
             Insert picture description here
 The following is a description of the fields of the C0 and S0 packets.
  Version: 8 bits
   In C0, this field indicates the RTMP version required by the client. In S0, this field indicates the RTMP version selected by the server. The version defined in this specification is 3; 0-2 is used by early products and has been discarded; 4-31 is reserved for future use; 32-255 is not allowed (in order to distinguish other text protocols starting with a certain character). If the service cannot recognize the version requested by the client, it should return 3. The client can choose to reduce to version 3 or choose to cancel the handshake.
3 C1 and S1 message formats
  C1 and S1 messages are 1536 bytes long and consist of the following fields.
          Insert picture description here
  Time: 4 bytes
   This field contains a time stamp. The timestamp should be the time starting point of the subsequent block of the endpoint that sent this data block. It can be 0, or any other value. In order to synchronize multiple streams, an endpoint may send the current value of its block stream.
  Zero: 4 bytes
    This field must be all zeros.
  Random data: 1528 bytes.
    This field can contain any value. Because each endpoint must distinguish its identity with the handshake initialized by itself and the handshake initialized by the peer, this data should have sufficient randomness. But there is no need to encrypt secure random values, or dynamic values.
4 C2 and S2 message formats
 C2 and S2 messages are 1536 bytes long. Just a reply from S1 and C1. This message consists of the following fields.
            Insert picture description here
  Time: 4 bytes
    This field must contain the time of peer-to-peer transmission (S1 for C2, C1 for S2).
  Time 2: 4 bytes
    This field must contain the timestamp of the packet previously sent and read by the peer.
  Random reply: 1528 bytes
    This field must contain the random data field sent by the opposite end (S1 for C2, C1 for S2).
Each peer can use the timestamp in the time and time 2 fields to quickly estimate bandwidth and delay. But this may not be practical.
5. Schematic diagram of handshake
             Insert picture description here

Source code

Note: There is a large amount of code in handshake.h, but a lot of the code is for processing the encrypted version of RTMP protocol, such as rtmps; therefore, we will not do too much analysis here, we only consider the ordinary RTMP protocol (rtmp.c in).

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
}

Guess you like

Origin blog.csdn.net/weixin_37921201/article/details/90047494