RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)
=====================================================
RTMPdump(libRTMP) 源代码分析系列文章:
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_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部分)的更多相关文章
- RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- Spark源代码分析之中的一个:Job提交执行总流程概述
Spark是一个基于内存的分布式计算框架.执行在其上的应用程序,依照Action被划分为一个个Job.而Job提交执行的总流程.大致分为两个阶段: 1.Stage划分与提交 (1)Job依照RDD之间 ...
- RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- RTMPdump(libRTMP) 源代码分析 9: 接收消息(Message)(接收视音频数据)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- Android在如何建立一个WebServer
今天老板交待任务最终完成了,感觉收获颇多,所以写一个关于它的记录,首先,看一下.老板的需求 需求: 希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,当中HTML页面可 ...
随机推荐
- Linux下的一些常用命令(一)
在Linux环境下敲各种命令是再正常不过了,尤其是现在大多少服务器均为Linux系统,但是我又记不住这么多命令,只是偶尔在项目做完发布到服务器上的时候会涉及到,所以在网上找了一些命令,在此记录一下~ ...
- Android Multimedia框架总结(五)多媒体基础概念
转载请把头部出处链接和尾部二维码一起转载,本文出自: http://blog.csdn.net/hejjunlin/article/details/52431887 上篇中介绍了MediaPlayer ...
- JQuery之动画与特效
显示与隐藏 show(spped,[callback])与hide(spped,[callback]) speed可选填slow.normal.fast,对应的速度分别为600ms.400ms.200 ...
- javascript之正则表达式
创建正则表达式的两种方法 显式: new RegExp("pattern"[,"flags"]); 例 var regex = new ("abc&q ...
- 使用 symbolicatecrash 解析崩溃堆栈
一.先引用一位牛人的博贴: xcode解crashlog工具symbolicatecrash的使用 在xocde编译app的时候会同时生成一个以dsym(该app对应的符号表)作为后缀的文件,每个ap ...
- hive的strict模式;where,group by,having,order by同时使用的执行顺序
主要限制三种情况 (1) 有partition的表查询需要加上where子句,筛选部分数据实现分区裁剪,即不允许全表全分区扫描,防止数据过大 (2) order by 执行时只产生一个reduce,必 ...
- Runtime系列(一)-- 基础知识
众所周知,Objective-C 是一种运行时语言.运行时怎么来体现的呢?比如一个对象的类型确定,或者对象的方法实现的绑定都是推迟到软件的运行时才能确定的.而运行时的诸多特性都是由Runtime 来实 ...
- linu下C语言之BMP图片操作编程(下)
前面提高了一个将BMP左转的程序,右转其实也是类似的操作,就不写了,这节,我们来实现,将一张BMP图进行灰度处理,代码贴上: #include <stdio.h> #include < ...
- Android初级教程图片信息
对图片常规信息要了解其性质.图片大小.像素.位图等等概念总结如下: 图片在计算机中的大小 图片的总大小 = 图片的总像素 * 每个像素占用的大小(图片的总像素=像素尺寸也就是分辨率,例如设定800*4 ...
- 应用UUID简化设计
应用UUID简化设计(金庆的专栏)UUID(Universally Unique Identifier) 保证每次生成的都是唯一的,不同机器生成UUID也能保证唯一.网游中使用UUID可以避免全局的I ...