2 Darwin流化服务器介绍 Darwin Streaming Server(简称DSS)是QuickTime Streaming Server开放式源代码的版本,同时支持FreeBSD、Linux、Solaris、Windows NT和Windows 2000等多个*作系统,是当前所有同类产品中支持平台最多的一个。 DSS的源代码和相关文档可从以下站点获得:http://www.apple.com DSS源代码完全采用标准C++语言写成,编程风格非常优秀,每个C++类都对应着一对和类同名的.h/.cpp文件。但是由于大量采用了面向对象的概念,如继承、多态等等;而且源文件和类相当多,所以不太容易讲清楚。 因此,读者最好事先把代码完整的过滤一两遍,再配合本文,就能看得更清楚点。整个服务器包括多个子系统,分别存放在独立的工程内,如图2所示。 其中,最为重要的是基础功能类库(CommonUtilitiesLib)和流化服务器(StreamingServer)两个工程,前者是整个系统的通用代码工具箱,包括了线程管理、数据结构、网络和文本分析等多个功能模块。DSS和其他相关的工具使用基础功能类库工程中定义的功能类实现以下三个目标: 这四个标准是开发所有流式媒体产品都必须掌握的,因此在对相关代码进行分析和二次开发之前,希望读者了解上述四种协议的基本思想,上述协议样本可从以下网站获得:http://www.ietf.org 3 基础功能类库(Common Utilities) 3.1 OS类 class OSMutex inline void Lock(); //加锁 private: 在Windows平台上,OSMutex类是通过临界区(CRITICAL_SECTION)来实现的,第10行定义了临界区变量fMutex。类实例化时构造函数调用InitializeCriticalSection(&fMutex)初始化临界区变量,对应的在析构函数中调用DeleteCriticalSection(&fMutex)清除。 void OSMutex::RecursiveLock() { // 当前线程已经拥有互斥量,只需增加引用计数 if (OSThread::GetCurrentThreadID() == fHolder) { fHolderCount++; //增加引用计数 return; } #ifdef __Win32__ ::EnterCriticalSection(&fMutex); //申请进入临界区 #else (void)pthread_mutex_lock(&fMutex); #endif Assert(fHolder == 0); fHolder = OSThread::GetCurrentThreadID(); //更新临界区拥有者标志 fHolderCount++; Assert(fHolderCount == 1); } 第1行检测如果当前线程已经拥有互斥量,就只需将内部计数fHolderCount加1,以便纪录正在使用互斥量的方法数。如果当前线程还没有得到互斥量,第7行调用EnterCriticalSection()函数申请进入临界区;如果当前已经有其他线程进入临界区,该函数就会阻塞,使得当前线程进入睡眠状态,直到占用临界区的线程调用LeaveCriticalSection(&fMutex)离开临界区后才可能被唤醒。一旦线程进入临界区后,它将首先更新临界区持有者标志(第12行),同时将临界区引用计数加1。 class OSCond inline void Signal(); //传信函数 private: 虽然同是用于线程同步,但OSCond类与OSMutex大不相同,后者用来控制对关键数据的访问,而前者则通过发信号表示某一*作已经完成。在Windows平台中,OSCond是通过事件(event)来实现的;构造函数调用CreateEvent()函数初始化事件句柄fCondition,而析构函数则调用CloseHandle()关闭句柄。 inline void OSCond::Broadcast() { //提示:本函数相当循环调用Signal()函数 #ifdef __Win32__ UInt32 waitCount = fWaitCount; //等待传信的用户数 for (UInt32 x = 0; x < waitCount; x++) //循环为每个用户传信 { BOOL theErr = ::SetEvent(fCondition); //设置事件句柄为有信号状态 Assert(theErr == TRUE); } //此处略… } Broadcast首先统计所有等待传信的用户数(第2行),然后用一个循环为每个用户传信(第3~7)行。这种编程方法虽然不是很优雅(elegant),但是由于Windows平台上不支持广播传信功能(Linux和Solaris均支持),也只好如此。 class OSThread OSThread(); //构造函数 //子类继承该纯虚函数完成自己的工作 void Start(); //启动线程 OSThread封装了线程的基本功能,一个OSThread的实例代表一个线程。用户通过继承OSThread,并且重载其中的纯虚函数Entry(第5行),从而将自己的任务交给该线程运行。OSThread内部运行机制比较复杂,为此我们用图3所示的流程来描述其运行过程。 template<class T, class K> class OSHashTable { /*提示:OSHashTable被设计成为一个类模版,两个输入参数分别为:class T:实际的对象类;class K:用于为class T计算哈希表键值的功能类。*/ public: OSHashTable( UInt32 size ) //构造函数,入参是哈希表中对象的最大个数 { fHashTable = new ( T*[size] ); //申请分配size个哈希对象class T的空间 Assert( fHashTable ); memset( fHashTable, 0, sizeof(T*) * size ); //初始化 fSize = size; /*下面的代码决定用哪种方式为哈希表的键值计算索引; 如果哈希表的大小不是2的幂,只好采用对fSize求余的方法; 否则可以直接用掩码的方式,这种方式相对速度更快*/ fMask = fSize - 1; if ((fMask & fSize) != 0) //fSize不是2的幂 fMask = 0; fNumEntries = 0; //当前对象数 } ~OSHashTable() //析构函数 { delete [] fHashTable; //释放空间 } //下面介绍向哈希表中添加一个class T对象的源代码 void Add( T* entry ) { Assert( entry->fNextHashEntry == NULL ); /*利用功能类class K,计算class T对象的哈希键值,其计算方法由用户在class K中定义*/ K key( entry ); UInt32 theIndex = ComputeIndex( key.GetHashKey() );//利用键值计算索引 entry->fNextHashEntry = fHashTable[ theIndex ]; //在新加对象中存储索引值 fHashTable[ theIndex ] = entry; //将该对象插入到索引指定的位置 fNumEntries++; / } //下面介绍从哈希表中删除一个class T对象的源代码 void Remove( T* entry ) { //首先从哈希表中找到待删除的对象 //1、计算哈希键值和其对应的对象索引 key( entry ); UInt32 theIndex = ComputeIndex( key.GetHashKey() ); T* elem = fHashTable[ theIndex ]; T* last = NULL; /*2、通过对象索引查找对象,如果不是要找的对象,接着找下一个,直到找到为止。这是因为,存放的时候就是按照这种模式计算索引的。*/ while (elem && elem != entry) { last = elem; elem = elem->fNextHashEntry; } //找到该对象,将其删除 if ( elem ) { Assert(elem); if (last) last->fNextHashEntry = elem->fNextHashEntry; else //elem在头部 fHashTable[ theIndex ] = elem->fNextHashEntry; elem->fNextHashEntry = NULL; fNumEntries--; } } //下面介绍从哈希表中查找一个class T对象的方法 T* Map( K* key ) //入参为哈希键值 { UInt32 theIndex = ComputeIndex( key->GetHashKey() ); //计算索引 T* elem = fHashTable[ theIndex ]; //找到索引对应的对象 while (elem) { K elemKey( elem ); if (elemKey =*key) //检查是否找对 break; elem = elem->fNextHashEntry; //如果不是,继续找下一个 } return elem; } //以下略… } 以上介绍了哈希表的构造以及三种基本*作:添加、删除和查询。另外,DSS还定义了OSHashTableIter类用于枚举OSHashTable中的class T对象;其中主要的*作有First和Next等,限于篇幅,此处就不再详述。 |