7.live555mediaserver-第1阶段小结(完整对象图和思维导图)

这是[手把手一起学live555]的第8篇(按这个序号看,请找正确顺序看)。
live555工程在我的gitee下(doc下有思维导图、drawio图):https://gitee.com/lure_ai/live555/tree/master

章节目录链接
0.前言——章节目录链接与为何要写这个?
https://blog.csdn.net/yhb1206/article/details/127259190?spm=1001.2014.3001.5502

学习demo
live555mediaserver.cpp

学习线索和姿势
1.学习的线索和姿势

网络编程
流媒体的地基是网络编程(socket编程)。
[网络编程学习]-0.学习路线。

绘图规则
本文的对象图和思维导图遵守的规则详见:
2.绘图规则

网络编程
流媒体的发动机 是网络编程(socket编程)。
学习路线[网络编程学习]-0.学习路线。

TCP非阻塞服务端网络编程流程
socket创建、bind、listen、select、accept、select、recvfrom/send、close。

rtsp协商流程
option、describe、setup、play、pause
、resume、teardown

本节内容和目标
第1阶段网络编程线索结束之际的总结。
最后把完整的对象图和思维导图放出来。

正式开始

前面几节知道了live555的服务端demo——
live555mediaserver.cpp——是TCP非阻塞服务端网络编程模式,跟着网络编程线索已经跟踪的差不多了。接下来,就要切换到新的阶段了——rtsp协议线索阶段——因此,有必要总结下第1阶段那些值得琢磨品味儿的事儿。

第1点 socket链表管理

socket的管理机制是建立了一个链表统一管理socket和对应的响应方法。如下图,链表头在最右边,队尾在最左边。
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第1张图片

哎呦,你说上图为何和前几节的链表图不一样呢?上图画的是至今最清晰的链表图,也是实际代码的映射图,前面几节我说过那些链表图是为了讲解网络编程线索、为了便于理解(好吧,我承认当时没有把所有链表队员看清楚,但不影响),我也说过在这个总结里放出完整的链表图。
如上图:有n个客户端已连接队员+4个服务端监听队员+1个链表头——共5+n个成员。——声明下成员和队员的区别——成员包含链表头,队员不包含链表头。

链表头——队长——就不说了,没啥可说的——啥数据也不保存,只为了遍历各链表队员。
接下来,看下4个服务端监听队员——从右到左依次排列:
第1个队员:
IPV4的绑定554/8554端口的服务端监听socket队员,保存的方法GenericMediaServer::incomingConnectionHandlerIPv4(void* instance, int /mask/),

第2个队员:
IPV6的绑定554/8554端口的服务端监听socket队员,保存的方法GenericMediaServer::incomingConnectionHandlerIPv6(void* instance, int /mask/),

第3个队员:
IPV4的绑定80/8000/8080端口的服务端监听socket队员,保存的方法RTSPServer::incomingConnectionHandlerHTTPIPv4(void* instance, int /mask/)

第4个队员:
IPV6的绑定80/8000/8080端口的服务端监听socket队员,保存的方法RTSPServer::incomingConnectionHandlerHTTPIPv6(void* instance, int /mask/)

这4个队员保存方法的类图如下,可视化下,感受下。
前两个rtsp标准socket链表队员保存的方法如下图:
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第2张图片
第3-4个rtsp over http的socket链表队员保存的方法如下图:
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第3张图片

n个客户端:
最左边我画了一个链表队员标注“n个客户端”——这是以一当n——不然你让我画n个图,那得多大?意思下得了!

接下来,剖析下链表图(其实链表图里面标注的已经够详细的了)。

4个队员的创建时机
前面2个队员是在main函数中创建第3个对象DynamicRTSPServer时在构造函数里创建的——先入股。
后面2个队员是在main函数中接下来创建rtsp-over-http时入队的——如下图在这里插入图片描述

n个客户端队员创建时机
当客户端对前面4个队员中的任意一个发起链接时,就会调用这第1个方法。这第1个方法accept调用“生出”新的客户端已链接socket,然后创建对象RTSPServer::RTSPClientConnection,这个对象会创建新的链表队员——标记为n个客户端的socket队员。

其实前面2个队员是标准rtsp——绑定端口554/8554端口,后面这个是rtsp-over-http——绑定80/8000/8080端口,可见live555支持的很全面。我把这4个队员叫做原始服务端链表队员,再加上链表头,初始化完,这个创业公司(链表队列)只有5个成员。——五大元老!

统一的链表队员创建入队等管理方法
因为添加新socket到select监听集、创建链表队员、保存socket和对应响应方法等操作是通用操作——不管服务端队员还是客户端队员——所以最好统一这样的操作——封装为一个方法,这样简洁不用重复造轮子——大道至简!只要是socket链表队员的操作最终都是调用这个方法:BasicTaskScheduler::setBackgroundHandling

第1个方法
注意注意注意,图上这4个队员保存的方法是不同的,但是但是但是,最终调用的都是同一个——真是殊途同归啊:GenericMediaServer::incomingConnectionHandlerOnSocket

第2个方法
当客户端对这4个队员中的任意一个发起链接时,就会调用这第1个方法。这第一个方法主要工作是accept调用“生出”新的客户端已链接socket,然后创建对象RTSPServer::RTSPClientConnection,这个对象会创建新的链表队员(在构造函数中把它的父类GenericMediaServer::ClientConnection的this指针传给envir().taskScheduler().setBackgroundHandling()——这个会创建这客户端链表队员),把这个客户端已链接socket和第2个方法装起来。——因为这4个链表队员执行的方法最终都是调用第1个方法——因此,这新诞生的客户端链表队员里保存的方法也是一样的——我把它称之为第2个方法
这第2个方法就是
GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /mask/)
这第2个方法就是所有新的客户端链表成员里保存的方法——不管是rtsp还是http最终数据的接受都是进到这个方法里的——客户端与服务端数据交互的地方。

因此,第1个方法创建的新的链表队员我称之为客户端链表队员——因为它是完成建立客户端与服务端的链接

第2个方法的调用时机:任一客户端向服务端传输数据,那么第1方法会创建。

总结来说,第1个方法是accept建立链接,第2个方法是recvfrom/send传输数据。——看吧,还是离不开网络编程流程。

说一句,不管标准的rtsp还是rtsp over http都最终统一到第1个方法和第2个方法,这种做法是省时省力的——不用重复造轮子了。

第1个比喻:4个原始服务端链表队员就相当于是母体,n个客户端链表队员都是这4和原始服务端链表队员“生”的——你看任何新的客户端发起链接都得执行第1个方法——一旦执行这方法,完了,它就生出来新的队员并给它打上第2个方法。——所以4个服务端队员和客户端队员的关系是母亲与孩子的关系。然后客户端向服务端发起数据传输就会调用第2个方法,第2个方法就来解析rtsp协议、http协议等等。

第2个比喻:4个原始服务端队员拿着一样的模具(第1个方法)刻出一模一样的客户端队员——来一个新的客户端链接就用这模具(第1个方法)刻出来一个新客户端链表队员。——就像批量生产一样!客户端链表队员就由这4个原始服务端链表队员批量生产出来的!

链表队员里clientData保存的this指针
链表图里标注的很清晰了。
前2个链表队员里clientData存放的是对象RTSPServer的父类GenericMediaServer的地址,后面2个链表队员里clientData存放的是对象RTSPServer的地址。如下思维导图。
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第4张图片
如上图,端口554/8554的IPv4或IPv6的socket链表队员中clientData保存的this指针是:DynamicRTSPServer对象的父类GenericMediaServer指针。因为在new DynamicRTSPServer时在其父类构造函数GenericMediaServer::GenericMediaServer中把this指针传给envir().taskScheduler().setBackgroundHandling()——这个会创建这个链表队员并保持方法和clientData。

同理,端口80/8000/8080的http的socket链表队员传下去的this指针是对象DynamicRTSPServer的指针。看传入的this在哪传的:
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第5张图片

而客户端链表队员里clientData存放的是创建这个链表队员的对象RTSPServer::RTSPClientConnection的父类GenericMediaServer::ClientConnection的指针。如下思维导图截取
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第6张图片
第1方法会创建对象RTSPServer::RTSPClientConnection,在构造函数中把它的父类GenericMediaServer::ClientConnection的this指针传给envir().taskScheduler().setBackgroundHandling()——这个会创建这客户端链表队员。——一个客户端链表队员对应一个专属的RTSPServer::RTSPClientConnection对象,n个客户端链表队员就有n个各自专属的RTSPServer::RTSPClientConnection这个对象,用以处理rtsp等数据传输。诺,这个对象就长这样,前面给过图的。
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第7张图片

最右边的就是RTSPServer::RTSPClientConnection这个对象的模样,我也画出它的fOurRTSPServer成员保持第3个对象DynamicRTSPServer的父类RTSPServer的引用。

第2点:网络编程线索节点梳理

socket创建、bind、listen这些节点是在DynamicRTSPServer的静态方法createNew里的setUpOurSocket方法里。

select是在BasicTaskScheduler::SingleStep里。

accept是在上面说的第1方法里的。

recvfrom/send-close是在上面说的第2个方法里的。

从这能知道live555也是按照这个网络编程流程来设计的这个框架——只不过它的实现是这样的,当然网上别的开源都各有各地实现——但本质都得按照网络编程的流程来组装和实现——万变不离其宗!——说到这我就要问你了:有没有听说过一种从天而降的掌法?如来神掌!——从网络编程线索为切入点把它拿下!

第3点:多个客户端建立链接场景

前面几节讲只有一个客户端发起链接的情形,如果这个时候我再打开一个vlc写上同样的url再拉一路流呢?会怎么样?答案很显然,那服务端就再提供一条流呗——url一样,那自然播放数据也一样。——可以参看第1点总结的链表图进行分析——n个客户端,我已经画出多个客户端链接情形了。

第4点 单线程机制

前几节也提到了,这个demo是个单线程——且依托于主线程,且只有这一个线程——啥叫主线程?main函数就是主线程。而调用glibc/uclibc等运行时库的线程创建接口(比如pthread_create)创建的线程,我们称之为普通线程。可以查看我相关博客。

这个单线程机制没有reactor模型好——将已链接的客户端分发到线程池中,再做个负载均衡更好。

第5点 完整对象图和思维导图

最后把前几节绘制的对象图和思维导图的整体图放出来如下——或者去我的gitee上doc下也能看到的。

(1)第1阶段完整对象图:

7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第8张图片

(2)第1阶段完整思维导图
7.live555mediaserver-第1阶段小结(完整对象图和思维导图)_第9张图片

你可能感兴趣的:(手把手一起学live555,学习,开源,网络协议,服务器)