LibRTMP源代码分析4

在RTMP客户端和服务器端网络层连接成功之后,第一件事情就是RTMP的握手。至于为什么要握手,协议本身没有太多的描述,我理解的握手目的:握手的主要目的就是让客户端和服务器端互相打个招呼,其主要内容应该包括看看两者是否同时理解RTMP协议本身(至少理解握手啊,呵呵)以及对RTMP协议版本的互认;至于是否理解RTMP协议本身的解决方案就是制造一些RTMP特有的数据和要求在客户端和服务器端来回折腾几次,如果折腾得顺利就算完事,不顺利,那就断绝关系吧!告诉协议版本当然在前面的折腾中顺便带上就行了。

        握手过程比较简单:
 a)  握手开始于客户端发送C0、C1块。服务器收到C0或C1后发送S0和S1。
 b)  当客户端收齐S0和S1后,开始发送C2。当服务器收齐C0和C1后,开始发送S2。
 c)  当客户端和服务器分别收到S2和C2后,握手完成。

       注意:Adobe在2009年公开了rtmp协议,wikipedia说是部分公开(an incomplete version)而且handshake也有变更。我们接下来要分析的握手是rtmp_specification_1.0中定义的握手方式,称之为simple handshake。而complex handshake是后来变更的方式,Adobe没有公开。若研发rtmp server,将h264数据给FP播放时,必须为complex handshake,否则数据能传输但无法播放。关于complex handshake的解释及源代码分析,可以参考blog: http://blog.csdn.net/win_lin/article/details/7714493

/**
 * @brief 客户端握手过程。 C0和C1放在一个buffer中发送出去,当客户端收齐
 *  S0和S1数据后,开始发送C2。当客户端收到S2后并验证,则客户端的握手完成。
 *  此处将就收到的S1原封不动的当成C2发送给服务器端。
 *
 *  C0/S0 (1个字节)  :表示版本号
 *  C1/S1 (1536字节):格式为  | time(4字节) | 0 0 0 0 (4字节) | 1528个随机数 |
 *  C2/S2 (1536字节):格式为  | time(4字节)(对等端发送的时间) |  time2 (4字节) | 1528个对等端随机回复 |
 *
 * @return 握手成功返回TRUE, 否则返回FALSE.
 */
static int HandShake(RTMP *r, int FP9HandShake)
{
int i, bMatch;
uint32_t uptime, suptime; // 客户端和服务器端时间
char type;
char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1; // clientbuf中存有C0和C1数据,clientsig只含有C1数据
char serversig[RTMP_SIG_SIZE];

// RTMP协议版本号为0x03,即C0数据 
clientbuf[0] = 0x03; /* not encrypted,没有加密 */

// 获取系统时间(毫秒为单位),将其写入到C1中,占4个字节
uptime = htonl(RTMP_GetTime());
memcpy(clientsig, &uptime, 4);

// 系统时间之后是4个字节的0,固定的 
memset(&clientsig[4], 0, 4);

#ifdef _DEBUG
// debug版,后面的1528个随机数简单的都设为0xff
for (i = 8; i < RTMP_SIG_SIZE; i++)
clientsig[i] = 0xff;
#else
// release版,使用rand()循环生成1528个伪随机数
for (i = 8; i < RTMP_SIG_SIZE; i++)
clientsig[i] = (char)(rand() % 256);
#endif
// 发送握手数据C0和C1 
if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1)) 
 return FALSE;

// 读取数据报,长度为1,存入type中
// 此处读取的是服务器端发送来的S0,表示服务器使用的Rtmp版本
if (ReadN(r, &type, 1) != 1) /* 0x03 or 0x06 (03是明文,06是加密,其他值非法)*/
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);

// 读取服务器端发送过来的S1数据,并判断随机序列长度是否相同
if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
return FALSE;

/* decode server response */

// 把serversig的前4个字节赋值给suptime
// S1中的time与C2中的time应该相同
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 */
// 发送握手数据C2(1536个字节)给服务器
if (!WriteN(r, serversig, RTMP_SIG_SIZE))
return FALSE;

// 读取从服务器发送过来的握手数据S2(1536个字节)到serversig
if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
return FALSE;

// 比较客户端C1和服务器端S2的1536个数是否匹配
bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
if (!bMatch)
RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);

return TRUE;
}

/**
 * @brief 服务端握手过程。 服务器收到C0或C1之后就发送S0和S1,当服务器端收齐
 *  C0和C1数据后,开始发送S2。当服务端收到C2后并验证,则服务端的握手完成。
 *  此处将就收到的C1原封不动的当成S2发送给客户端。
 *
 * @return 握手成功返回TRUE, 否则返回FALSE.
 */
static int SHandShake(RTMP *r)
{
int i;
char serverbuf[RTMP_SIG_SIZE + 1], *serversig = serverbuf + 1; // serverbuf中存有S0和S1数据,serversig只含有S1数据
char clientsig[RTMP_SIG_SIZE];
uint32_t uptime;
int bMatch;

// 读取从客户端发送过来的C0,存入serverbuf[0]中,表示要求服务器使用的Rtmp版本
if (ReadN(r, serverbuf, 1) != 1) /* 0x03 or 0x06 */
return FALSE;

RTMP_Log(RTMP_LOGDEBUG, "%s: Type Request  : %02X", __FUNCTION__, serverbuf[0]);

// RTMP版本号目前只支持0x03
if (serverbuf[0] != 3)
{
RTMP_Log(RTMP_LOGERROR, "%s: Type unknown: client sent %02X", __FUNCTION__, serverbuf[0]);
return FALSE;
}

// 获取系统时间(毫秒为单位),将其写入到S1中,占4个字节
uptime = htonl(RTMP_GetTime());
memcpy(serversig, &uptime, 4);

// 系统时间之后是固定的4字节0
memset(&serversig[4], 0, 4);

#ifdef _DEBUG
// debug版,后面的1528个随机数简单的都设为0xff
for (i = 8; i < RTMP_SIG_SIZE; i++)
serversig[i] = 0xff;
#else
// release版,使用rand()循环生成1528个伪随机数
for (i = 8; i < RTMP_SIG_SIZE; i++)
serversig[i] = (char)(rand() % 256);
#endif

// 发送握手数据S0和S1
if (!WriteN(r, serverbuf, RTMP_SIG_SIZE + 1))
return FALSE;

// 读取客户端发送过来的C1数据,并判断随机序列长度是否相同
if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
return FALSE;

/* decode client response */

// 把clientsig的前4个字节赋值给uptime
// C1的time与S2中time应该相同
memcpy(&uptime, clientsig, 4);

// 将网络字节序转化为主机字节序,即大端转小端
uptime = ntohl(uptime);

RTMP_Log(RTMP_LOGDEBUG, "%s: Client Uptime : %d", __FUNCTION__, uptime);
RTMP_Log(RTMP_LOGDEBUG, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__, clientsig[4], clientsig[5], clientsig[6], clientsig[7]);

/* 2nd part of handshake */
// 发送握手数据S2(1536个字节)给客户端
if (!WriteN(r, clientsig, RTMP_SIG_SIZE))
return FALSE;

// 读取从客户端发送过来的握手数据C2(1536个字节)到clientsig
if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
return FALSE;

// 比较客户端C2和服务器端S1的1536个数是否匹配
bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
if (!bMatch)
RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);

return TRUE;
}

猜你喜欢

转载自blog.csdn.net/wongainia158158/article/details/48625103
今日推荐