版本說明
本文分析基于2019-12-30的版本。(Live555沒有版本管理?)
1 Hash容器
1.1 HashTable
HashTable 定義了Hash表的接口。
- 成員函數(shù) Add()、Remove() 和 Lookup() 分別用于添加、刪除和查找元素。

1.2 HashTable
BasicHashTable實(shí)現(xiàn)了HashTable接口。這是hash表的一個標(biāo)準(zhǔn)實(shí)現(xiàn)。
- 成員fBuckets[]指向一個TableEntry*的數(shù)組。其中每個元素指向一個TableEntry鏈表。TableEntry包括一個(key,value)對。
- 成員函數(shù) hashIndexFromKey()計(jì)算key的HASH值。它將Key值進(jìn)行若干變換,然后限縮在數(shù)組fBuckets的索引范圍內(nèi)。
- 成員函數(shù)keyMatches()用于比較兩個key值是否相同。
- insertNewEntry()向Hash表添加新元素。
- 它調(diào)用hashIndexFromKey()計(jì)算在數(shù)組fBucket中的索引,然后創(chuàng)建新的TableEntry實(shí)例,并掛接到該索引位置上的TableEntry鏈表。
- TableEntry的key值一般有兩種類型:string和unsigned。兩種類型計(jì)算HASH值的方法不同。
- 如果Hash表中的TableEntry實(shí)例數(shù)超過指定的閾值,則調(diào)用rebuild()重建Hash表,將成員fBuckets[]加長。這樣做的目的是保證Hash表的效率。

2 任務(wù)調(diào)度
2.1 TaskScheduler
TaskScheduler定義任務(wù)調(diào)度器接口。

TaskScheduler接口涵蓋三種任務(wù):Socket監(jiān)聽、延時任務(wù)、和事件通知任務(wù)。后面依次說明這三種任務(wù)。
- 成員函數(shù)setBackgroundHandling()創(chuàng)建Socket監(jiān)聽任務(wù)。
- 成員函數(shù)createEventTrigger()創(chuàng)建事件通知任務(wù),triggerEvent()觸發(fā)任務(wù)。
- 成員函數(shù)scheduleDelayedTask()創(chuàng)建延時任務(wù)。
- 成員函數(shù)doEventLoop()處理任務(wù)。
2.2 HandlerDescritor、HandlerSet - Socket監(jiān)聽任務(wù)
HandlerDescriptor保存socket、以及與socket關(guān)聯(lián)的處理函數(shù)。

- 成員socketNum是socket的值
- 成員conditionSet是fd_set值的組合
- handlerProc是指定的處理函數(shù),clientData是handlerProc的參數(shù)。
這些成員分別指定應(yīng)該監(jiān)控Socket上的什么事件(可讀、可寫、異常),事件發(fā)生時應(yīng)該調(diào)用什么函數(shù)處理它。
HandlerSet是HandlerDescriptor的集合。
如下圖,HandlerSet的成員fHandlers保存一個HandlerDescriptor雙向列表。

對于HandlerDescriptor,
- 成員fPrevHandler和fNextHandler用于鏈接雙向鏈表。
對于HandlerSet,
- 成員函數(shù)assignHandler()設(shè)置Socket的處理函數(shù)。創(chuàng)建新的HandlerDescriptor實(shí)例,或者更新已有的實(shí)例。創(chuàng)建HandlerDescriptor實(shí)例時,將它鏈接在雙向鏈表的尾部。
- 成員函數(shù)clearHandler()清除Socket的處理函數(shù)。在HandlerDescriptor的析構(gòu)函數(shù)中,把它自身從雙向鏈表中移除。
- 成員函數(shù)lookupHandler()查找Socket的處理函數(shù)。
HanderIterator基于HandlerDescriptor雙向鏈表。
- 調(diào)用它的成員函數(shù)next(),可以遍歷HandlerDescriptor鏈表。
2.3 AlarmHandler - 延時任務(wù)
DelayQueueEntry保存延時任務(wù),而DelayQueue保存一個DelayQueueEntry雙向鏈表。

對于DelayQueueEntry,
- 成員fDeltaTimeRemaining保存延時的時長,這是一個相對值。
- 成員fPrev和fNext 讓DelayQueueEntry的多個實(shí)例能鏈成一個雙向列表。
- 虛擬成員函數(shù)handleTimeout()是延時到達(dá)時執(zhí)行的函數(shù)。DelayQueueEntry的派生類可以定制自己的實(shí)現(xiàn)。
DelayQueue是一個DelayQueueEntry容器。
- 它從DelayQueueEntry派生。這樣的設(shè)計(jì)看起來只是為了不需要為起始節(jié)點(diǎn)加一個DelayQueryEntry成員。(讓人迷惑的設(shè)計(jì)?。?/li>
- 成員函數(shù)addEntry()、updateEntry()、和removeEntry()分別用于增加、更新和移除元素。
AlarmHandler是DelayQueueEntry的派生類,實(shí)現(xiàn)了虛擬成員函數(shù)handleTimeout()。
- 成員fProc是處理函數(shù),由handleTimeout()調(diào)用。成員fClientData是fProc的參數(shù)。兩者在AlarmHandler的構(gòu)造函數(shù)中作為參數(shù)指定。
在DelayQueueEntry雙向鏈表中,元素的延時fDeltaTimeRemaining是相對的,是前一個元素的相對值。
如下圖中,有4個元素,它們的fDeltaTimeRemaining值分別是4、4、3和2,那么第一個元素的實(shí)際延時是最近的,是4,其他元素的實(shí)際延時依次累加,分別是8、11和13。

DelayQueueEntry和DelayQueue用Timeval度量時間。Timeval的派生類DelayInterval和_EventTime,是對Timeval的簡單包裝。

2.4 事件通知任務(wù)
沒有定義專門的類處理事件通知任務(wù)。BasicTaskScheduler0類直接處理這類任務(wù)。
2.5 BasicTaskScheduler0、BasicTaskScheduler
BasicTaskSchduler0實(shí)現(xiàn)了TaskScheduler接口,實(shí)現(xiàn)延時任務(wù)和事件監(jiān)聽任務(wù)。
BasicTaskScheduler派生自BasicTaskScheduler0,進(jìn)一步實(shí)現(xiàn)socket監(jiān)聽任務(wù)。

對于BasicTaskSchduler0,
- 實(shí)現(xiàn)shceduleDelayedTask()創(chuàng)建延時任務(wù)。成員fDelayQueue保存一組延時任務(wù)。
- 實(shí)現(xiàn)createEventTrigger()和triggerEvent(),創(chuàng)建、觸發(fā)事件通知任務(wù)。成員fTriggerEventHandlers[]保存一組事件通知任務(wù)。
- BasicTaskScheduler0定義了新接口,也就是新的虛擬函數(shù)SingleStep()。它實(shí)現(xiàn)了虛擬函數(shù)doEventLoop()。在里面,它在循環(huán)中調(diào)用singleStep()。
對于BasicTaskScheduler,
- 實(shí)現(xiàn)setBackgroundHandling()創(chuàng)建Socket監(jiān)聽任務(wù)。BasicTaskScheduler0的成員fHandlers保存socket處理函數(shù)。(為什么不是BasicTaskSchduler的成員?)
- 實(shí)現(xiàn)BasicTaskScheduler0定義的接口SingleStep()。

在BasicTaskScheduler::SingleStep()中,
- 調(diào)用select(),監(jiān)聽HandlerSet中保存的socket的可讀、可寫和異常信號。如果有信號,則調(diào)用指定HandlerSet實(shí)例中指定的處理函數(shù)TaskScheduler::BackgroundHandleProc()處理。
- 檢查fTriggerEventHandlers[]中的任務(wù)是否為觸發(fā)狀態(tài),如果是,則調(diào)用指定處理函數(shù)TaskFunc()進(jìn)行處理。
- 調(diào)用DelayQueue::handleAlarm(),檢查成員fDelayQueue中延時任務(wù)是否到達(dá),如果是,調(diào)用處理函數(shù)TaskFunc()進(jìn)行處理。
3 程序上下文
3.1 UsageEnvironment
UsageEnvironment定義了一個輸出打印消息的接口。
- 成員函數(shù)setResultMsg()將打印消息保存起來,getResultMsg()獲得保存的打印信息, reportBackgroundError()輸出保存的打印信息。
- operator <<() 一系列成員函數(shù),輸出指定格式的數(shù)據(jù)。
BasicUsageEnvironment0實(shí)現(xiàn)了這個接口的setResultMsg()、getResultMsg()和reportBackgroundError()。
- 成員fResultMsgBuffer[]保存打印消息。
- reportBackgroundError()向stderr輸出打印消息。
BasicUsageEnvironment進(jìn)一步實(shí)現(xiàn)了這個接口的operator<<()系列函數(shù)。
- 這些函數(shù)向stderr輸出打印消息。

UsageEnvironment還是整個程序運(yùn)行的上下文。
- 成員fScheduler引用全局唯一的TaskScheduler實(shí)例
- 成員liveMediaPriv引用全局唯一的_Tables實(shí)例,保存了兩個全局表。
- 成員groupsockPriv實(shí)際上是_groupsockPriv實(shí)例,保存幾個組播全局變量,其中reuseFlag是綁定Socket時是否重用地址。
應(yīng)用程序只創(chuàng)建一個UsageEnvironment實(shí)例。
3.2 _Tables - 全局容器
_Tables保存幾個hash表。
- _Tables是singleton模式,調(diào)用getOurTables()可以得到唯一的實(shí)例。
- 成員mediaTable是其中一個Hash表,它是MediumLookupTable的實(shí)例。

3.3 Medium、MediaLookupTable
Medium給它的派生類的實(shí)例保存一個名字。
- 成員fMediumName[]保存這個名字。這個名字調(diào)用MediaLookupTable::generateNewName()得到。
- Medium還引用唯一的UsageEnvironment實(shí)例。
MediumLookupTable保存一個Medium集合。這個集合保存在成員fTable中,這是一個HashTable實(shí)例。
- addNew()、lookup()和remove()分別向hashtable加入、查找和刪除Medium實(shí)例。
