首页 > 学院 > 开发设计 > 正文

RTMP学习(九)rtmpdump源码阅读(4)建立连接

2019-11-06 08:50:56
字体:
来源:转载
供稿:网友

建立连接

    建立连接的步骤如下:

    1、设置套接字的地址信息    2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手    3、调用RTMP_Connect1,进行rtmp内部的握手操作和网络连接操作。

    建立连接的入口函数:

/*** 1、设置套接字的地址信息** 2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手** 3、调用RTMP_Connect1,进行rtmp内部的握手操作*/intRTMP_Connect(RTMP *r, RTMPPacket *cp){  struct sockaddr_in service;  if (!r->Link.hostname.av_len)    return FALSE;  memset(&service, 0, sizeof(struct sockaddr_in));  service.sin_family = AF_INET;  // 设置套接字地址  if (r->Link.socksport)    {      /* Connect via SOCKS */      if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))	return FALSE;    }  else    {      /* Connect directly */      if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))	return FALSE;    }	// 建立套接字连接  if (!RTMP_Connect0(r, (struct sockaddr *)&service))    return FALSE;  r->m_bSendCounter = TRUE;  // 进行rtmp内部的握手过程  return RTMP_Connect1(r, cp);}

建立套接字连接

    建立套接字连接的步骤:    1、创建tcp类型的套接字    2、调用connect连接到服务器    3、如果有必要就调用SocksNegotiate,它用来判断客户端与服务器之间能否收发数据    4、设置tcp的TCP_NODELAY选项,即禁用Nagle算法,禁用之后可以达到更好的实时性。

/*** 建立套接字的链接** 套接字的连接是所有通信的基础*/intRTMP_Connect0(RTMP *r, struct sockaddr * service){  int on = 1;  r->m_sb.sb_timedout = FALSE;  r->m_pausing = 0;  r->m_fDuration = 0.0;  r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, ipPROTO_TCP);  if (r->m_sb.sb_socket != -1)    {      if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0)	{	  int err = GetSockError();	  RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)",	      __FUNCTION__, err, strerror(err));	  RTMP_Close(r);	  return FALSE;	}      if (r->Link.socksport)	{	  RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);	  if (!SocksNegotiate(r))	    {	      RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);	      RTMP_Close(r);	      return FALSE;	    }	}    }  else    {      RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__,	  GetSockError());      return FALSE;    }  /* set timeout */  {    SET_RCVTIMEO(tv, r->Link.timeout);    if (setsockopt        (r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))      {        RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",	    __FUNCTION__, r->Link.timeout);      }  }  setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));  return TRUE;}

判断客户端与服务器之间能否收发数据

static intSocksNegotiate(RTMP *r){  unsigned long addr;  struct sockaddr_in service;  memset(&service, 0, sizeof(struct sockaddr_in));  add_addr_info(&service, &r->Link.hostname, r->Link.port);  addr = htonl(service.sin_addr.s_addr);  {	  // 构造数据包    char packet[] = {      4, 1,			/* SOCKS 4, connect */      (r->Link.port >> 8) & 0xFF,      (r->Link.port) & 0xFF,      (char)(addr >> 24) & 0xFF, (char)(addr >> 16) & 0xFF,      (char)(addr >> 8) & 0xFF, (char)addr & 0xFF,      0    };				/* NULL terminate */	// 发送数据    WriteN(r, packet, sizeof packet);	// 接收回应    if (ReadN(r, packet, 8) != 8)      return FALSE;    if (packet[0] == 0 && packet[1] == 90)      {        return TRUE;      }    else      {        RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", __FUNCTION__, packet[1]);        return FALSE;      }  }}

进行rtmp内部的握手操作和连接操作

    RTMP_Connect1函数执行rtmp协议内部的握手和连接操作!

/*** RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作*/intRTMP_Connect1(RTMP *r, RTMPPacket *cp){  if (r->Link.protocol & RTMP_FEATURE_SSL)    {#if defined(CRYPTO) && !defined(NO_SSL)      TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl);      TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);      if (TLS_connect(r->m_sb.sb_ssl) < 0)	{	  RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);	  RTMP_Close(r);	  return FALSE;	}#else      RTMP_Log(RTMP_LOGERROR, "%s, no SSL/TLS support", __FUNCTION__);      RTMP_Close(r);      return FALSE;#endif    }  if (r->Link.protocol & RTMP_FEATURE_HTTP)    {      r->m_msgCounter = 1;      r->m_clientID.av_val = NULL;      r->m_clientID.av_len = 0;      HTTP_Post(r, RTMPT_OPEN, "", 1);      if (HTTP_read(r, 1) != 0)	{	  r->m_msgCounter = 0;	  RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__);	  RTMP_Close(r);	  return 0;	}      r->m_msgCounter = 0;    }  RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);  if (!HandShake(r, TRUE))    {      RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__);      RTMP_Close(r);      return FALSE;    }  RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__);  // 建立网络连接操作,注意cp是null!  if (!SendConnectPacket(r, cp))    {      RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__);      RTMP_Close(r);      return FALSE;    }  return TRUE;}

rtmp内部的握手操作

    RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作

    客户端的握手操作如下:    1、发送C0和C1    2、接收S0    3、接收S1    4、发送C2    5、接收S2

/*** 客户端的握手操作*/static intHandShake(RTMP *r, int FP9HandShake){  int i;  uint32_t uptime, suptime;  int bMatch;  char type;    // 将要发送给服务器的数据块,简称C  char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;    // 从服务器接收到的数据块,简称S  char serversig[RTMP_SIG_SIZE];  // 第一个字节填充0x03,表示开始标识  clientbuf[0] = 0x03;		/* not encrypted */  // 取得当前时间  uptime = htonl(RTMP_GetTime());  // 把时间填入数据块C中  memcpy(clientsig, &uptime, 4);  memset(&clientsig[4], 0, 4);#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  // 把数据块C发送给服务器,发送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);  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 */  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  if (!WriteN(r, serversig, RTMP_SIG_SIZE))    return FALSE;  // 接收S2  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)    return FALSE;  bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);  if (!bMatch)    {      RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);    }  return TRUE;}

rtmp内部的网络连接操作

发送“connect命令”

    rtmp内部的网络连接操作是通过SendConnectPacket函数执行的!但是请注意,这个函数里只发送了“connect”命令,剩余的连接步骤在RTMP_ConnectStream中进行。先分析SendConnectPacket:

// 注意rtmp内部建立网络连接的时候,参数cp是nullstatic intSendConnectPacket(RTMP *r, RTMPPacket *cp){  RTMPPacket packet;  char pbuf[4096], *pend = pbuf + sizeof(pbuf);  char *enc;  if (r->Link.CombineConnectPacket)    r->Link.ConnectPacket = TRUE;  if (cp)    return RTMP_SendPacket(r, cp, TRUE);  packet.m_nChannel = 0x03;	/* control channel (invoke) */  packet.m_headerType = RTMP_PACKET_SIZE_LARGE;  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;  packet.m_nTimeStamp = 0;  packet.m_nInfoField2 = 0;  packet.m_hasAbsTimestamp = 0;  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;  // 构建连接命令  enc = packet.m_body;  enc = AMF_EncodeString(enc, pend, &av_connect); // connect命令  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);  *enc++ = AMF_OBJECT;  enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app);  if (!enc)    return FALSE;  if (r->Link.protocol & RTMP_FEATURE_WRITE)    {      enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate);      if (!enc)	return FALSE;    }  if (r->Link.FlashVer.av_len)    {      enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer);      if (!enc)	return FALSE;    }  if (r->Link.swfUrl.av_len)    {      enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl);      if (!enc)	return FALSE;    }  if (r->Link.tcUrl.av_len)    {      enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl);      if (!enc)	return FALSE;    }  if (!(r->Link.protocol & RTMP_FEATURE_WRITE))    {      enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);      if (!enc)	return FALSE;      enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);      if (!enc)	return FALSE;      enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);      if (!enc)	return FALSE;      enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs);      if (!enc)	return FALSE;      enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0);      if (!enc)	return FALSE;      if (r->Link.pageUrl.av_len)	{	  enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl);	  if (!enc)	    return FALSE;	}    }  if (r->m_fEncoding != 0.0 || r->m_bSendEncoding)    {	/* AMF0, AMF3 not fully supported yet */      enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);      if (!enc)	return FALSE;    }  if (enc + 3 >= pend)    return FALSE;  *enc++ = 0;  *enc++ = 0;			/* end of object - 0x00 0x00 0x09 */  *enc++ = AMF_OBJECT_END;  /* add auth string */  if (r->Link.auth.av_len)    {      enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH);      if (!enc)	return FALSE;      enc = AMF_EncodeString(enc, pend, &r->Link.auth);      if (!enc)	return FALSE;    }  if (r->Link.extras.o_num)    {      int i;      for (i = 0; i < r->Link.extras.o_num; i++)	{	  enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend);	  if (!enc)	    return FALSE;	}    }  packet.m_nBodySize = enc - packet.m_body;  // 发送数据包  return RTMP_SendPacket(r, &packet, TRUE);}

处理rtmp内部网络连接的其他步骤

    首先要知道,处理rtmp内部网络连接的其他步骤的函数不是RTMP_Connect,而是RTMP_ConnectStream。

    RTMP_ConnectStream函数的功能是在播放开始之前不断的读取数据包,然后分析数据包的内容,如果有必要的的话就进行解析和处理。

    其中RTMP_ConnectStream通过调用RTMP_ClientPacket函数来分析处理服务器发送来的数据包。所以先分析一下这个比较核心的处理函数:

/*** 解析服务器发送来的packet*/intRTMP_ClientPacket(RTMP *r, RTMPPacket *packet){	int bHasMediaPacket = 0;	switch (packet->m_packetType)	{		// 设置块大小	case RTMP_PACKET_TYPE_CHUNK_SIZE:		/* chunk size */		HandleChangeChunkSize(r, packet);		break;		// 报告	case RTMP_PACKET_TYPE_BYTES_READ_REPORT:		/* bytes read report */		RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);		break;		// 用户的控制命令	case RTMP_PACKET_TYPE_CONTROL:		/* ctrl */		HandleCtrl(r, packet);		break;		// 确认窗口大小 Window Acknowledgement Size	case RTMP_PACKET_TYPE_SERVER_BW:		/* server bw */		HandleServerBW(r, packet);		break;		// 设置带宽	case RTMP_PACKET_TYPE_CLIENT_BW:		/* client bw */		HandleClientBW(r, packet);		break;		// 音频数据	case RTMP_PACKET_TYPE_AUDIO:		/* audio data */		/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */		HandleAudio(r, packet);		bHasMediaPacket = 1;		if (!r->m_mediaChannel)			r->m_mediaChannel = packet->m_nChannel;		if (!r->m_pausing)			r->m_mediaStamp = packet->m_nTimeStamp;		break;		// 视频数据	case RTMP_PACKET_TYPE_VIDEO:		/* video data */		/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */		HandleVideo(r, packet);		bHasMediaPacket = 1;		if (!r->m_mediaChannel)			r->m_mediaChannel = packet->m_nChannel;		if (!r->m_pausing)			r->m_mediaStamp = packet->m_nTimeStamp;		break;		// flex 流发送(AMF3编码)	case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:		/* flex stream send */		RTMP_Log(RTMP_LOGDEBUG,			"%s, flex stream send, size %u bytes, not supported, ignoring",			__FUNCTION__, packet->m_nBodySize);		break;		// flex共享对象(AMF3编码)	case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:		/* flex shared object */		RTMP_Log(RTMP_LOGDEBUG,			"%s, flex shared object, size %u bytes, not supported, ignoring",			__FUNCTION__, packet->m_nBodySize);		break;		// flex消息(AMF3编码)	case RTMP_PACKET_TYPE_FLEX_MESSAGE:		/* flex message */	{		RTMP_Log(RTMP_LOGDEBUG,			"%s, flex message, size %u bytes, not fully supported",			__FUNCTION__, packet->m_nBodySize);		/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */		/* some DEBUG code */#if 0		RTMP_LIB_AMFObject obj;		int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);		if(nRes < 0) {			RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);			/*return; */		}		obj.Dump();#endif		if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1)			bHasMediaPacket = 2;		break;	}		// info数据(AMF0编码)	case RTMP_PACKET_TYPE_INFO:		/* metadata (notify) */		RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__,			packet->m_nBodySize);		// 处理元数据		if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))			bHasMediaPacket = 1;		break;	// 共享对象	case RTMP_PACKET_TYPE_SHARED_OBJECT:		RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring",			__FUNCTION__);		break;		// 命令消息	case RTMP_PACKET_TYPE_INVOKE:		/* invoke */		RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,			packet->m_nBodySize);		/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */		// 处理命令		if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1)			bHasMediaPacket = 2;		break;		// Flash视频	case RTMP_PACKET_TYPE_FLASH_VIDEO:	{		/* go through FLV packets and handle metadata packets */		unsigned int pos = 0;		uint32_t nTimeStamp = packet->m_nTimeStamp;		while (pos + 11 < packet->m_nBodySize)		{			uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1);	/* size without header (11) and prevTagSize (4) */			if (pos + 11 + dataSize + 4 > packet->m_nBodySize)			{				RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!");				break;			}			if (packet->m_body[pos] == 0x12)			{				HandleMetadata(r, packet->m_body + pos + 11, dataSize);			}			else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9)			{				nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4);				nTimeStamp |= (packet->m_body[pos + 7] << 24);			}			pos += (11 + dataSize + 4);		}		if (!r->m_pausing)			r->m_mediaStamp = nTimeStamp;		/* FLV tag(s) */		/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */		bHasMediaPacket = 1;		break;	}	default:		RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,			packet->m_packetType);#ifdef _DEBUG		RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);#endif	}	return bHasMediaPacket;}

    为了弄懂这些操作流程,最好手动调试,经过调试之后可以发现libRtmp处理网络连接的步骤(除去发送“connect”命令)如下:

    1、HandleServerBW函数被调用,处理服务器发送过来的“确认窗口大小”

    2、HandleClientBW函数被调用,处理服务器发送过来的“设置带宽”

    3、HandleCtrl函数被调用,处理服务器发来的“stream begin”

    4、HandleInvoke函数被调用,处理服务器发来的“result结果”

    5、调用RTMP_SendServerBW函数,发送“确认窗口大小”

    6、调用RTMP_SendCtrl(r, 3, 0, 300),暂时不清楚该函数调用的目的

    对比网络连接建立的时序图和理论上的操作流程,可以看到基本上是差不多的

处理服务器发来的“确认窗口大小”

static voidHandleServerBW(RTMP *r, const RTMPPacket *packet){	r->m_nServerBW = AMF_DecodeInt32(packet->m_body);	RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r->m_nServerBW);}

处理服务器发来的“设置带宽”

static voidHandleClientBW(RTMP *r, const RTMPPacket *packet){	r->m_nClientBW = AMF_DecodeInt32(packet->m_body);	if (packet->m_nBodySize > 4)		r->m_nClientBW2 = packet->m_body[4];	else		r->m_nClientBW2 = -1;	RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r->m_nClientBW,		r->m_nClientBW2);}

处理服务器发来的“stream begin”

static voidHandleCtrl(RTMP *r, const RTMPPacket *packet){	short nType = -1;	unsigned int tmp;	if (packet->m_body && packet->m_nBodySize >= 2)		nType = AMF_DecodeInt16(packet->m_body);	RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,		packet->m_nBodySize);	/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */	if (packet->m_nBodySize >= 6)	{		switch (nType)		{		case 0:			// 处理”stream begin“			tmp = AMF_DecodeInt32(packet->m_body + 2);			RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp);			break;		// 其他代码省略***		}	}	// 其他代码省略***}

处理服务器发来的“result”

    处理“result”的时候,判断这是不是“connect”命令的结果,如果是,那么:

    1、调用RTMP_SendServerBW函数,给服务器发送“确认窗口大小”

    2、调用RTMP_SendCtrl(r, 3, 0, 300),暂时不清楚该函数调用的目的

/*** 处理命令消息** 这些命令消息是使用AMF0格式进行编码的*/static intHandleInvoke(RTMP *r, const char *body, unsigned int nBodySize){	AMFObject obj;	AVal method;	double txn;	int ret = 0, nRes;	char pbuf[256], *pend = pbuf + sizeof(pbuf), *enc, **params = NULL;	char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";	char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";	int param_count;	AVal av_Command, av_Response;	if (body[0] != 0x02)		/* make sure it is a string method name we start with */	{		RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",			__FUNCTION__);		return 0;	}	nRes = AMF_Decode(&obj, body, nBodySize, FALSE);	if (nRes < 0)	{		RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);		return 0;	}	AMF_Dump(&obj);	AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);	txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));	RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val);	// 处理”result“	if (AVMATCH(&method, &av__result))	{		AVal methodInvoked = { 0 };		int i;		for (i = 0; i < r->m_numCalls; i++) {			if (r->m_methodCalls[i].num == (int)txn) {				methodInvoked = r->m_methodCalls[i].name;				AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);				break;			}		}		if (!methodInvoked.av_val) {			RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",				__FUNCTION__, txn);			goto leave;		}		RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__,			methodInvoked.av_val);		// ”connect“命令完成		if (AVMATCH(&methodInvoked, &av_connect))		{			if (r->Link.token.av_len)			{				AMFObjectProperty p;				if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p))				{					DecodeTEA(&r->Link.token, &p.p_vu.p_aval);					SendSecureTokenResponse(r, &p.p_vu.p_aval);				}			}			if (r->Link.protocol & RTMP_FEATURE_WRITE)			{				SendReleaseStream(r);				SendFCPublish(r);			}			else			{				// 向服务器发送”确认窗口大小“				RTMP_SendServerBW(r);				RTMP_SendCtrl(r, 3, 0, 300);			}			if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))			{				AVal av_requestaccess = AVC("requestAccess");				AVal av_auth = AVC("h§4jhH43d");				enc = pbuf;				enc = AMF_EncodeString(enc, pend, &av_requestAccess);				enc = AMF_EncodeNumber(enc, pend, 0);				*enc++ = AMF_NULL;				enc = AMF_EncodeString(enc, pend, &av_auth);				av_Command.av_val = pbuf;				av_Command.av_len = enc - pbuf;				SendCustomCommand(r, &av_Command, FALSE);				AVal av_getConnectionCount = AVC("getConnectionCount");				enc = pbuf;				enc = AMF_EncodeString(enc, pend, &av_getConnectionCount);				enc = AMF_EncodeNumber(enc, pend, 0);				*enc++ = AMF_NULL;				av_Command.av_val = pbuf;				av_Command.av_len = enc - pbuf;				SendCustomCommand(r, &av_Command, FALSE);				SendGetStreamLength(r);			}			else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua"))			{				SendGetStreamLength(r);			}			else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")				|| strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in"))			{				AVal av_r = AVC("r");				enc = pbuf;				enc = AMF_EncodeString(enc, pend, &av_r);				enc = AMF_EncodeNumber(enc, pend, 0);				*enc++ = AMF_NULL;				av_Command.av_val = pbuf;				av_Command.av_len = enc - pbuf;				SendCustomCommand(r, &av_Command, FALSE);				SendGetStreamLength(r);			}			else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com"))			{				AVal av_ModelName;				AVal av_CheckPublicStatus = AVC("CheckPublicStatus");				if (strlen(pageUrl) > 7)				{					strsplit(pageUrl + 7, FALSE, '/', ¶ms);					av_ModelName.av_val = params[1];					av_ModelName.av_len = strlen(params[1]);					enc = pbuf;					enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);					enc = AMF_EncodeNumber(enc, pend, 0);					*enc++ = AMF_NULL;					enc = AMF_EncodeString(enc, pend, &av_ModelName);					av_Command.av_val = pbuf;					av_Command.av_len = enc - pbuf;					SendCustomCommand(r, &av_Command, FALSE);				}			}			/* Weeb.tv specific authentication */			else if (r->Link.WeebToken.av_len)			{				AVal av_Token, av_Username, av_PassWord;				AVal av_determineAccess = AVC("determineAccess");				param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', ¶ms);				if (param_count >= 1)				{					av_Token.av_val = params[0];					av_Token.av_len = strlen(params[0]);				}				if (param_count >= 2)				{					av_Username.av_val = params[1];					av_Username.av_len = strlen(params[1]);				}				if (param_count >= 3)				{					av_Password.av_val = params[2];					av_Password.av_len = strlen(params[2]);				}				enc = pbuf;				enc = AMF_EncodeString(enc, pend, &av_determineAccess);				enc = AMF_EncodeNumber(enc, pend, 0);				*enc++ = AMF_NULL;				enc = AMF_EncodeString(enc, pend, &av_Token);				enc = AMF_EncodeString(enc, pend, &av_Username);				enc = AMF_EncodeString(enc, pend, &av_Password);				av_Command.av_val = pbuf;				av_Command.av_len = enc - pbuf;				RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);				SendCustomCommand(r, &av_Command, FALSE);			}			else				RTMP_SendCreateStream(r);		}		else if (AVMATCH(&methodInvoked, &av_getStreamLength))		{			RTMP_SendCreateStream(r);		}		else if (AVMATCH(&methodInvoked, &av_createStream))		{			r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));			if (!(r->Link.protocol & RTMP_FEATURE_WRITE))			{				/* Authenticate on Justin.tv legacy servers before sending FCSubscribe */				if (r->Link.usherToken.av_len)					SendUsherToken(r, &r->Link.usherToken);				/* Send the FCSubscribe if live stream or if subscribepath is set */				if (r->Link.subscribepath.av_len)					SendFCSubscribe(r, &r->Link.subscribepath);				else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))					SendFCSubscribe(r, &r->Link.playpath);			}			if (r->Link.protocol & RTMP_FEATURE_WRITE)			{				SendPublish(r);			}			else			{				if (r->Link.lFlags & RTMP_LF_PLST)					SendPlaylist(r);				SendPlay(r);				RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);			}		}		else if (AVMATCH(&methodInvoked, &av_play) ||			AVMATCH(&methodInvoked, &av_publish))		{			r->m_bPlaying = TRUE;		}		free(methodInvoked.av_val);	}	else if (AVMATCH(&method, &av_onBWDone))	{		if (!r->m_nBWCheckCounter)			SendCheckBW(r);	}	// 删除其他代码***	// ***	// 删除其他代码***leave:	AMF_Reset(&obj);	return ret;}

经过上述的一系列步骤,rtmp内部的网络连接就算完成了!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表