=====================================================

RTMPdump(libRTMP) 源代码分析系列文章:

RTMPdump 源代码分析 1: main()函数

RTMPDump (libRTMP) 源代码分析2:解析RTMP地址——RTMP_ParseURL()

RTMPdump (libRTMP) 源代码分析3: AMF编码

RTMPdump (libRTMP) 源代码分析4: 连接第一步——握手 (HandShake)

RTMPdump (libRTMP) 源代码分析5: 建立一个流媒体连接  (NetConnection部分)

RTMPdump (libRTMP) 源代码分析6: 建立一个流媒体连接  (NetStream部分 1)

RTMPdump (libRTMP) 源代码分析7: 建立一个流媒体连接  (NetStream部分 2)

RTMPdump (libRTMP) 源代码分析8: 发送消息 (Message)

RTMPdump (libRTMP) 源代码分析9: 接收消息 (Message)  (接收视音频数据)

RTMPdump (libRTMP) 源代码分析10: 处理各种消息 (Message)

=====================================================

函数调用结构图

RTMPDump (libRTMP)的整体的函数调用结构图如下图所示。

单击查看大图

详细分析

本篇文章分析一下RTMPdump里面的建立一个流媒体连接过程中的函数调用。

之前已经简单分析过流媒体链接的建立过程:

RTMP流媒体播放过程

而且分析过其函数调用过程:

RTMPDump源代码分析 0: 主要函数调用分析

在这里就不详细叙述了,其实主要是这两个函数:

RTMP_Connect()

RTMP_ConnectStream()

第一个函数用于建立RTMP中的NetConnection,第二个函数用于建立RTMP中的NetStream。一般是先调用第一个函数,然后调用第二个函数。

下面先来看看RTMP_Connect():

注意:贴上去的源代码是修改过的RTMPdump,我添加了输出信息的代码,形如:r->dlg->AppendCInfo("建立连接:第0次连接。开始建立Socket连接");改代码不影响程序运行,可忽略。

RTMP_Connect()

//连接
int
RTMP_Connect(RTMP *r, RTMPPacket *cp)
{
	//Socket结构体
  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)
    {
	//加入地址信息
      /* 使用SOCKS连接 */
      if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))
	return FALSE;
    }
  else
    {
      /* 直接连接 */
      if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))
	return FALSE;
    }
  //-----------------
  r->dlg->AppendCInfo("建立连接:第0次连接。开始建立Socket连接");
  //-----------------------------
  if (!RTMP_Connect0(r, (struct sockaddr *)&service)){
	r->dlg->AppendCInfo("建立连接:第0次连接。建立Socket连接失败");
    return FALSE;
  }
  //-----------------
  r->dlg->AppendCInfo("建立连接:第0次连接。建立Socket连接成功");
  //-----------------------------
  r->m_bSendCounter = TRUE;

  return RTMP_Connect1(r, cp);
}

我们可以看出调用了两个函数RTMP_Connect0()以及RTMP_Connect1()。按照按先后顺序看看吧:

RTMP_Connect0()

//sockaddr是Linux网络编程的地址结构体一种,其定义如下:
//struct sockaddr{
//	unsigned short sa_family;
//	char sa_data[14];
//};
//说明:sa_family:是地址家族,也称作,协议族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET。
//	sa_data:是14字节协议地址。
//有时不使用sockaddr,而使用sockaddr_in(多用在windows)(等价)
//struct sockaddr_in {
//	short int sin_family;              /* Address family */
//	unsigned short int sin_port;       /* Port number */
//	struct in_addr sin_addr;           /* Internet address */
//	unsigned char sin_zero[8];         /* Same size as struct sockaddr */
//};
//union {
//   struct{
//      unsigned char s_b1,s_b2,s_b3,s_b4;
//         } S_un_b;
//    struct {
//      unsigned short s_w1,s_w2;
//         } S_un_w;
//    unsigned long S_addr;
//   } S_un;
//} in_addr;
//第0次连接,建立Socket连接
int
RTMP_Connect0(RTMP *r, struct sockaddr * service)
{
  int on = 1;
  r->m_sb.sb_timedout = FALSE;
  r->m_pausing = 0;
  r->m_fDuration = 0.0;
  //创建一个Socket,并把Socket序号赋值给相应变量
  //-----------------
  r->dlg->AppendCInfo("建立连接:第0次连接。create一个Socket");
  //-----------------------------
  r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (r->m_sb.sb_socket != -1)
    {

	//定义函数 int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);   
	//函数说明 connect()用来将参数sockfd 的Socket(刚刚创建)连至参数serv_addr
	//指定的网络地址。参数addrlen为sockaddr的结构长度。
	//连接
	RTMP_LogPrintf("建立Socket连接!\n");
	//-----------------
	r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket");
	//-----------------------------
      if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0)
	{
		//-----------------
		r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket失败");
		//-----------------------------
	  int err = GetSockError();
	  RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)",
	      __FUNCTION__, err, strerror(err));
	  RTMP_Close(r);
	  return FALSE;
	}
	  //-----------------
	  r->dlg->AppendCInfo("建立连接:第0次连接。connect该Socket成功");
	  //-----------------------------
	  //指定了端口号。注:这不是必需的。
      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;
}

可见RTMP_Connect0()主要用于建立Socket连接,并未开始真正的建立RTMP连接。

再来看看RTMP_Connect1(),这是真正建立RTMP连接的函数:

RTMP_Connect1()

//第1次连接,从握手开始
int
RTMP_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((SSL *)r->m_sb.sb_ssl, r->m_sb.sb_socket);
      if (TLS_connect((SSL *)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
    }
  //使用HTTP
  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);
      HTTP_read(r, 1);
      r->m_msgCounter = 0;
    }
  RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);
  //握手----------------
  r->dlg->AppendCInfo("建立连接:第1次连接。开始握手(HandShake)");
  //-----------------------------
  RTMP_LogPrintf("开始握手(HandShake)!\n");
  if (!HandShake(r, TRUE))
    {
		//----------------
		r->dlg->AppendCInfo("建立连接:第1次连接。握手(HandShake)失败!");
		//-----------------------------
      RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__);
      RTMP_Close(r);
      return FALSE;
    }
  //----------------
  r->dlg->AppendCInfo("建立连接:第1次连接。握手(HandShake)成功");
  //-----------------------------
  RTMP_LogPrintf("握手(HandShake)完毕!\n");
  RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__);
  //发送“connect”命令--------------
  //----------------
  r->dlg->AppendCInfo("建立连接:第1次连接。开始建立网络连接(NetConnection)");
  //-----------------------------
  RTMP_LogPrintf("开始建立网络连接(NetConnection)!\n");
  //----------------
  r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Connect)。");
  //-----------------------------
  if (!SendConnectPacket(r, cp))
    {
		//----------------
		r->dlg->AppendCInfo("建立连接:第1次连接。建立网络连接(NetConnection)失败!");
		//-----------------------------
      RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__);
      RTMP_Close(r);
      return FALSE;
    }
  //----------------
  r->dlg->AppendCInfo("建立连接:第1次连接。建立网络连接(NetConnection)成功");
  //-----------------------------
  RTMP_LogPrintf("命令消息“Connect”发送完毕!\n");
  return TRUE;
}

该函数做了以下事情:

HandShake()完成握手,之前已经分析过:RTMPdump 源代码分析 4: 连接第一步——握手(Hand Shake)

SendConnectPacket()发送包含“connect”命令的数据报,用于开始建立RTMP连接。具体该函数是怎么调用的,以后有机会再进行分析。

至此RTMP_Connect()分析完毕。

rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561

rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163

RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)的更多相关文章

  1. RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  2. RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  3. Spark源代码分析之中的一个:Job提交执行总流程概述

    Spark是一个基于内存的分布式计算框架.执行在其上的应用程序,依照Action被划分为一个个Job.而Job提交执行的总流程.大致分为两个阶段: 1.Stage划分与提交 (1)Job依照RDD之间 ...

  4. RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  5. RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  6. RTMPdump(libRTMP) 源代码分析 9: 接收消息(Message)(接收视音频数据)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  7. RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  8. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  9. Android在如何建立一个WebServer

    今天老板交待任务最终完成了,感觉收获颇多,所以写一个关于它的记录,首先,看一下.老板的需求 需求: 希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,当中HTML页面可 ...

随机推荐

  1. maven跳过单元测试的两个参数区别

    maven在打包过程中需要执行单元测试.但有些时候单元测试已经通过只是想打包时,想跳过测试.maven提供了两个参数跳过测试:maven.test.skip=true 和skipTests. 例子 m ...

  2. 如何构建Android MVVM 应用框架

    概述 说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架.然而两者的概念是不一样的,不能混为一谈.MVVM是一种架构模式,而DataBinding是 ...

  3. Tomcat集群如何同步会话

    Tocmat集群中最重要的交换信息就是会话消息,对某个tomcat实例某会话做的更改要同步到集群其他tomcat实例的该会话对象,这样才能保证集群所有实例的会话数据一致.在tribes组件的基础上完成 ...

  4. DVB数字电视系统简介(DVB-C,DVB-S,DVB-T)

    前一段时间在<通信原理>期末的时候研究了一下DVB数字电视系统.视音频编解码这些技术都是属于"信源"的技术,而<通信原理>研究的范围正好是它的补集,属于&q ...

  5. Collections类解析

    最常用的排序: 需要实现Comparable接口 1.什么是Comparable接口 此接口强行对实现它的每个类的对象进行整体排序.此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它 ...

  6. J2EE进阶(四)Spring配置文件详解

    J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程 ...

  7. 使用jquery获取radio的值

     使用jquery获取radio的值,最重要的是掌握jquery选择器的使用,在一个表单中我们通常是要获取被选中的那个radio项的值,所以要加checked来筛选,比如有以下的一些radio项: ...

  8. 【java虚拟机系列】java中类与对象的加载顺序

    首先了解一下Java虚拟机初始化的原理. JVM通过加装.连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用.类型的生命周期如下图所示: 装载和连接必须在初始化之前就要完成. 类 ...

  9. 精通CSS+DIV网页样式与布局--CSS段落效果

    在上一篇博文中,小编主要详细的介绍了CSS是如何控制文字的显示效果,随着需求的不断变更,那么我们如何对段落进行相关操作,以达到我们想要的效果呢,接下来,为了需要,小编继续来完善CSS对段落的控制的显示 ...

  10. 轻松学习Asp.net中的控件

    C/S 结构,即大家熟知的客户机和服务器结构.它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销.目前大多数应用软件 ...