DarwinStreamingServer程序结构分析_第1页
DarwinStreamingServer程序结构分析_第2页
DarwinStreamingServer程序结构分析_第3页
DarwinStreamingServer程序结构分析_第4页
DarwinStreamingServer程序结构分析_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Darwin Streaming Server程序结构分析Darwin Streaming Server是Apple公司提供的开源实时流媒体播放服务器程序。整个程序使用C+编写,在设计上遵循高性能,简单,模块化等程序设计原则,务求做到程序高效,可扩充性好。本文简述了程序的整个结构,目的是为了以后阅读及修改程序的方便。1. 前言Darwin Streaming Server是Apple公司提供的开源实时流媒体播放服务器程序。整个程序使用C+编写,在设计上遵循高性能,简单,模块化等程序设计原则,务求做到程序高效,可扩充性好。本文简述了程序的整个结构,目的是为了以后阅读及修改程序的方便。解开程序,程

2、序的主要目录结构及作用如下APIModules/ 模块程序的目录APIStubLib/ 程序公共接口类目录CommonUtilitiesLib/ 通用库Server.tproj/ 主程序目录QTFileLib/ mov, MP4文件读写库RTSPClientLib/ RTSP客户端协议库RTCPUtilitiesLib/ RTCP协议库PlaylistBroadcaster.tproj/ MP4播放列表广播器2. 重要基类结构每个C+程序总有自己的基本类库结构,这种结构一般是程序的工具类,及基本接口类。在Darwin Streaming Server中CommonUtilitiesLib目录下

3、是放着程序的基本类库结构主要重要的是:Task类,TaskThread类,TaskThreadPool类:这三个类封装了线程库。三个类相互作用,通过Friend Class,使表现在外端的只有Task类,在这里,我们可以这样看:Task就是CPU使用片断,TaskThreadPool是CPU管理程序,TaskThread就是CPU。这样就使我们只需关心Task类就可,至于怎么调度,这是TaskThread及TaskThreadPool的事了。下面我们来看这三个类是怎么互相作用从而形成一个整体的。a. Virtual TaskThread:Entry():这个函数是整个线程函数的第一个执行函数,

4、线程入口函数是TaskThread 从父类继承过来的Static OSThread:_Entry(),这个静态函数的唯一作用是执行Entry这个函数,这时的this指针是传进来的参数。这样就实现了线程的类封装。TaskThread:Entry()函数是一个循环,它不停的调用本身的WaitForTask函数,从自己内部的Queue中取得要运行的Task类指针。然后运行其中的Task:Run()函数。b. Virtual TaskThread:WaitForTask() 这个函数负责不停的从内部的队列中取到Task才返回。c. static TaskThread *TaskThreadPool:

5、sTaskThreadArray. 这个变量是一个很重要的变量,通过它,我们就能在线程外与线程内通信,(要知道TaskThread的指针在线程类就是this指针,在线程外就是sTaskThreadArray的里面放的值。这样的话,我们就可以通过这个指针(或者说死了,也就是这个队列)来进行通讯。d. void Task:Signal() 这个函数就是我们要往TaskThread队列里面加Task的函数.这个函数直接访问TaskThreadPool:sTaskThreadArray指针,并且因此直接访问到TaskThread的内部队列变量。e. Virtual int Task:Run() 这个函

6、数就是我们程序要运行的任务,也就是程序要在这里做什么。在这里程序的返回值是一个重要的信息,当返回-1时程序会直接删除该Task指针,当返回一个正数n 时线程会在 n毫秒后再次调用这个Task的Run函数,当返回0时,线程并不删掉这个Task指针,只是接着执行下一个Task.(注意; 在我们继承Task类,而重写Run虚函数时,我们第一件要作的事情是运行Task类的GetEvent()函数。)在Task,TaskThread, TaskThreadPool 三个类的结构中,通过友元包装了TaskThread.TaskThreadPool,只留下了Task给我们重写,这是一种类的重要组合方法。Ta

7、sk (OutThread) TaskThread(InThread)EventContext类, EventThread类:这两个类也像上面的类一样,组合成一个事件发生及线程的包装类。我们可以把它看作一个Select的封装调用。这个线程就是专门用来监测事件的发生的(当然也可以直接在这里处理罗)。类的框架及主要函数如下a. Virtual void EventThread:Entry(): 这个就是线程运行空间函数。 这个函数就是循环调用Select等待事件的发生。当事件发生时,它从内部Table中取得它的指针(这些指针都是在请求事件发生是调用函数注册进去的,这种指针必须是EventConte

8、xt的指针。也就是说事件处理类必须是EventContext的父类)。当取得EventContext的指针后,线程接着调用EventContext-ProcessEvent().b. Virtual void EventContext:ProcessEvent(): 这个就是处理事件函数,这个处理事件函数的缺省调用就是掉用本身的Task *EventContext:fTask 变量的Signal函数。这是通过构建或EventContext:SetTask()函数设的,联想到上面的Task,TaskThread,TaskThreadPool三者的关系,这个Signal函数就是将运行处理函数交给别

9、的线程处理的唯一函数,但是你也可以改写掉这个函数,可以让他直接在这个线程处理就完了。当然这样会大大降低响应的速度。看到这里,整个程序的线程调度框架你大概可以猜出来了吧。c. Void EventContext:RequestEvent(): 这个函数其实是事件请求函数,通过这个函数我们就相当于表达这样一种意思,当我们请求的事件发生的时候,请调用我们的ProcessEvent函数,也就是调用运行我们重写的Task:Run()函数。整个事件的处理流程是:I: EventContext:RequestEvent()II: EventThread:Entry() III:当事件触发时:EventCon

10、text:ProcessEvent()IIII: Task:Signal();IIIII: Task:Run();注:I,II, III,IIII是在监听线程中发生的,而IIIII是在处理线程中发生的。d. 这样的话,我们就实现了专用线程select函数的封装调用。TCPListenerSocket类:这个类其实就是上面EventThread的衍生使用,他专门用于包装Accept()调用。当Accept发生时,也就是select Read事件的促发。他产生一个TCPSocket类并调用本身的Virtual TCPListenerSocket:GetSessionTask()(这是一个纯虚函数)

11、产生一个Task处理类,并且帮他注册一个kRead事件。1. 程序框架结构程序的基本框架:a. 从线程划分来讲:程序可以理解为共有5个线程(在Linux下看起来是6个,因为Linux是进程模拟线程,所以其中有一个是管理线程)。(1) EventMoitor线程:这就是我刚才在EventContext,EventThread结构中讲的EventThread线程,他的主要功能就是监视事件的发生,当事件发生时调用EventContext的ProcessEvent()函数,(也就是调用Task:Signal()函数),把任务加入到线程池TaskThreadPool处理中去,TaskThread会调用T

12、ask:Run()自动处理的。同时这个线程也负责了Accept调用,TCPLisnterSocket(TCPListenerSocket类本身也是一个Socket类)也加入到这个调用中去,当kRead事件发生时,ProcessEvent()调用GetSessionTask()产生一个Task子类和Socket子类。并将Task类加入到这个Socket类中(顺便说一下,这个Socket类是继承EventContext类的,他本身有一个socketfd,因此它本质上是围绕select调用而使用的。)同时为这个Socket类注册kRead事件(其实就是将描述符加入到select 的ReadSet中去

13、)。(注:当你看ProcessEvent()函数时,你可能会以为ProcessEvent是一个死循环.其实这里并不是一个死循环,我们需要注意的是在程序中所有的Socket都是UnBlock方式,所以当再没有连接时,这个循环会从Break这个地方跳出去。追求每一点都是Unblock的,让程序不在任何一点阻塞,这是这个程序的一个很鲜明的特点。我们用线程替代进程,使用Select调用,追求的难道不是这一点吗。当处理繁忙时,把每一个CPU时间片都用在处理任务,这就是最高效的服务器程序所追求的。无点阻塞,这是程序所追求的,但也很可能形成这样一种情况,在空闲时,程序会形成死循环,以至于这个线程占用CPU过

14、多,导致CPU负载过高,这个问题程序用了两个方法来解决,1.在处理完一系列事件后,使用ThreadYield()调用让出CPU时间。(这种方法可能在有些系统不能使用,例如Linux就是无效的,因为在Linux线程本质上就是线程,它得到调度的机会和其他进程一样。因此只有在某些协作型的线程,例如Solaris的线程库,不将线程等同于进程来调度时(可以在程序中设置),即,线程和进程调度优先级不一样时(也就是所有的线程共享一个进程的CPU时间片),这样这个调用才真正有效。)2。就是Select调用的Timeout参数。(1) IdleTask线程:这是一个空闲线程,这相当于让CPU去干别的事情,他是通

15、过OsCond和OsMutex来完成这个功能的(在不同的*作系统有不同的实现,例如在标准PosixThread是pthread_cond_wait,)。我们可以通过IdleTask: SetIdleTimer()函数来实现这个定时任务。在到时间了才唤醒,这样的话,当一个Task子类,例如TCPListenerSocket类它多重继承了TCPSocket类和IdleTask,当这个线程要等一段时间再执行时。TCPListenerSocket就调用TCPListnerSocket:SetIdleTimer(),即IdleTask:SetIdleTimer(),来实现一段定时。当前线程是被IdleT

16、hread这个线程阻塞的。(至于程序为什么这样写,我猜想是为了是方便的实现一种定时机制及让出CPU时间,还有为什么要为了这个而单独产生一个线程,这也可能是为了使用的方便吧!)(1) TaskThread线程:终于进入了真正干事的进程了。这个就是所有Task的执行线程了,他的作用就是不停的执行Task()里面的Run()函数,根据Run函数的返回值,再决定对Task如何处理。在程序的框架中,对于单个CPU,TaskThreadPool只产生一个线程,也就是一个TaskThread线程。(多个线程会产生上下文切换的问题,)但对于多个CPU则产生多个TaskThread线程(这点大家都明白吧)。(注

17、:其实不同的问题有不同的解法,对于不会阻塞I/O上的程序我认为这一点是好的,越少的线程就意味着越少的上下文切换,也就意味这CPU化更多的时间在干正事上,但当线程中可能有单点阻塞时,这就在一定程度上会阻碍其他Task的执行,所以在写程序的时候一定要注意不能在任何一点阻塞)。因为在线程中要涉及文件读写I/O,所以我对在这里单CPU采用单线程在这里这一点持保留意见。不过作者在这里也考虑到了这个问题,你还记得Task:Run()函数的返回值吗,当为正数n时,这就相当于一个在n毫秒之后再次调用,这在一定程度上可以减缓线程的阻塞,但这个n值实在是很难确定的。如果不这样做,那应该怎么设计呢?一个连接一个线程

18、,这肯定是不可取的,在连接数少的情况下,这的确是很好的,但连接数一大也不行了,线程池应该是最好的选择,当也许多个线程的线程池也许更好,但也不是越多越好,这个数字的确定,我觉得应该是动态的,而不是静态的,就像apache那样)。注:系统也可以安排多个TaskThread线程来处理流任务。(2) 主进程:这是一个进程。你想要它干什么就干什么。在程序中这是一个监视进程,在这里主要是纪录一些系统信息。(3) Wait进程:这个进程就是等待主进程的返回,没什么意义。b. 从类的结构来划分:程序的主要结构是:QTSServer这个类就是程序的主运行类。在Darwin Streaming Server程序中

19、基本上所有的类都继承于QTSSDictionary类。QTSSDictionary这个类基本上它就是提供这样一个功能,它提供了像QTSSDictionary:GetValue(),QTSSDictionary:SetValue(),QTSSDictionary:AddInstanceAttribute(), QTSSDictionary:RemoveInstanceAtrribute()这样的函数,目的是为了提供一种动态实现属性的功能(即我们可以在任何时候加入这个类的成员变量或去掉这个类的成员变量。)。他在内部其实是通过一个复杂的类嵌套来实现的。(其实我觉得很奇怪,它为什么要用这么复杂的嵌套结

20、构来实现这样一种功能呢?)。通过继承这个类,我们就可以得到动态属性的功能。所以QTSServer的继承关系是这样的QTSServer - QTSServerInterface -QTSSDictionary - QTSStream在四层结构中:QTSServerInterface类是其实是QTSServer类的补充,它提供了一个Server所具有的所有成员变量。其实我们在这里可以将它看成是QTSServer的一个补充。在这里千万不要被QTSServerInterface这个名字给迷惑了,本来这两个类是可以合并成一个类,但作者可能基于类结构清晰的原因,而将它拆成了两层结构。另外还具有重要意义的类

21、还有两个,RTSPSession类,RTSPRequest类,RTPSession类。RTSPSession与RTSPRequest和RTPSession这三者的关系是通过复合的形式结合在一起的,即RTSPRequest和RTPSession是RTSPSession类的成员变量.(在这里有必要讲一下整个流媒体播放器与服务器的交互过程。1Client Player -RTSP (TCP a) - Streaming Server,2Client Player Streaming Server.4Client Player -RTCP(UDP c) - Streaming Server.5Client Player - RTP(UDP d) - Streaming Server.6Client Player -RTCP(UDP e) - Streaming Server.c. 从重要函数上划分:在Darwin Streaming Server中最重要的函数莫过于RTSPSession:Run()这个函数。这个函数将

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论