这是[手把手一起学live555]的第4篇(按这个序号看,请找正确顺序看)。 live555工程在我的gitee下(doc下有思维导图、drawio图): https://gitee.com/lure_ai/live555/tree/master
学习demo live555mediaserver.cpp
学习线索和姿势: 1.学习的线索和姿势
网络编程 流媒体的地基是网络编程(socket编程)。 可以参考我的[网络编程学习]-0.学习路线。
绘图规则 本文的对象图和思维导图遵守的规则详见: 2.绘图规则
本节内容和目标 (1)live555MediaServer.cpp的main函数大致流程分析, (2)思维导图绘制 (3)TCP非阻塞服务端网络编程流程从socket创建追踪到listen(TCP非阻塞服务网络编程流程:socket创建、bind、listen、select、accept、select、recv/send) (4)对象图绘制
正式开始
live555MediaServer.cpp的main函数,剖离干扰项如下:
int main(int argc, char** argv)
{
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
RTSPServer* rtspServer;
portNumBits rtspServerPortNum = 554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
if (rtspServer == NULL)
{
rtspServerPortNum = 8554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
}
if (rtspServer == NULL)
{
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}`
*env << "Play streams from this server using the URL\n";
if (weHaveAnIPv4Address(*env))
{
char* rtspURLPrefix = rtspServer->ipv4rtspURLPrefix();
*env << "\t" << rtspURLPrefix << "<filename>\n";
delete[] rtspURLPrefix;
if (weHaveAnIPv6Address(*env)) *env << "or\n";
}
if (weHaveAnIPv6Address(*env))
{
char* rtspURLPrefix = rtspServer->ipv6rtspURLPrefix();
*env << "\t" << rtspURLPrefix << "<filename>\n";
delete[] rtspURLPrefix;
}
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080))
{
*env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling).)\n";
}
else
{
*env << "(RTSP-over-HTTP tunneling is not available.)\n";
}
env->taskScheduler().doEventLoop();
return 0;
}
大致思维导图如下: main主要创建了3个类对象,既然我的线索是TCP非阻塞服务端网络编程流程,那么我就优先找它,我首先会问socket创建在哪呢?在哪bind端口呢?listen在哪呢? 如下思维导图: 图3-1 服务端网络编程起始流程所在
网络编程线索 如图3-1所示, 原来服务端网络编程起始流程是在DynamicRTSPServer的静态方法createNew里的setUpOurSocket方法里呢!。 你说:哇,这么快就找到了!——是的!揭秘就是这么快准稳狠!
那么具体是怎样的呢?下面我一一列举出来。
1.创建socket是在哪呢? 原来是在GenericMediaServer::setUpOurSocket里调用的函数setupStreamSocket里调用函数createSocket创建socket的呢!尹圣雨将创建socket比喻为安装电话机,有趣有趣。
2.bind在哪呢? 在GenericMediaServer::setUpOurSocket里调用的函数setupStreamSocket里调用bind系统调用绑定554或8554端口! 尹圣雨将bind比喻为给电话机设置电话号码,有趣有趣。
3.listen在哪呢? 原来在GenericMediaServer::setUpOurSocket进行listen监听,变成服务端网络编程。尹圣雨将创建listen比喻为给电话机连通电话线,等待别人打电话过来,有趣有趣。
网络编程关键点 如上面思维导图3-1所示,有个关键点是bind之后在listen之前,live555把socket设置成了一个非阻塞的socket。 非阻塞?啥意思?为啥要非阻塞呢?有啥用呢?这就是关键点。为了加速流媒体服务端的并发性能建议设置为非阻塞,这样客户端断开链接,你这边立马就断开了,否则响应很慢——比如在单元测试场景下测试客户端链接和断开链接频繁切换,你这会不会响应不及时?那这就得靠这个非阻塞了! 那这个网络编程就变成了TCP非阻塞服务端网络编程模式。 针对这个模式,它有它的固定套路和流程。按照TCP非阻塞服务端网络编程模式的流程,接下来就应该是监听这个socket, 一般是在一个线程里select这个socket,然后再调用accept查询是否有客户端链接。因此接下来的流程就是select套接字查询客户端链接。那我把这部分第一个select操作放到下一节。因为这涉及到了一个链表。
本节非阻塞服务端网络编程流程已经从socket创建追踪到listen了,接下来讲解下DynamicRTSPServer::createNew里做了什么操作: (1)调用2次setUpOurSocket方法。 (2)创建DynamicRTSPServer对象。 如下图
setUpOurSocket方法 DynamicRTSPServer::createNew调用了2次setUpOurSocket方法,这个方法内容就是上面第一时间讲过的服务端网络编程的起始流程所在(从socket创建到listen),具体回头看上面。 现在宏观跳出来讲下:第一次调用是创建IPV4,第二次调用是创建IPV6。注意细节,只要IPV4创建成功就不会创建IPV6。原因在于setUpOurSocket方法一开头就有判断,IPV4成功的话,再去创建IPV6就会直接返回而不会去创建IPV6了。
静态方法createNew调用了setUpOurSocket方法,而这个方法是哪个类的方法呢?是DynamicRTSPServer类的父类的父类GenericMediaServer的静态方法。 然后你问你怎么知道它是“父类的父类的方法”的?好,上图!DynamicRTSPServer类的类图如下:DynamicRTSPServer继承自RTSPServer,RTSPServer继承自GenericMediaServer,GenericMediaServer继承自Medium。 而setUpOurSocket方法是GenericMediaServer类的静态方法。你自己可以找下黑色的setUpOurSocket静态成员,数数它是不是DynamicRTSPServer的父类的父类的静态方法?!
c++静态成员语法
注意注意,此时还没有创建DynamicRTSPServer对象呢,怎么就能调用它们的方法呢?哦,这就涉及到c++的静态成员的知识了。静态成员和类对象无关只与类有关。从这个实例可以知道,在类对象未创建前你可以调用它的静态方法,甚至也可以调用它父类的父类(祖父/爷爷)的静态方法,注意一定要是静态方法呀。
c++的protected语法
这里其实还有一个普遍存在于live555的一个语法——protected语法——你看前面讲在DynamicRTSPServer::createNew里进行了new DynamicRTSPServer ,那有没有想过为何不把这个new DynamicRTSPServer拿出来直接这样写呢?对了,就是c++的protect语法在作梗——DynamicRTSPServer类的构造函数是放在protected里的。如下图: protected就是不让你直接new这个类进行创建对象的——直接在外面new编译会报错,这是protected的限制范围,你只能通过类的成员new出类对象——这在live555里普遍存在——BasicTaskScheduler类、BasicUsageEnvironment类、DynamicRTSPServer类等这3兄弟都是,当然也有其他类也是这样。 具体作用,我想可能就是为了安全吧——不让你随便创建类对象——我也是第一次见把构造函数保护起来的,不太懂,后续再说。
小结
本节以服务端网络编程为线索,从socket创建探索到了listen节点,并碰到了c++的静态成员和protected的语法知识,绘制了对象图,而且也知道了live555的服务端网络编程模式是TCP非阻塞服务端网络编程模式,下一个流程节点是第一次select。下节见。
|