physicalsocketserver
webrtc/base/physicalsocketserver.h /physicalsocketserver.cc 文件實現(xiàn)了一個基本的多路信號分離器吵取。這個多路信號分離器的實現(xiàn)代碼可以橫跨 Windows日缨、Linux、OSX玉控、Android灶挟、ios 等平臺坏逢,非常復雜。其實在 /base 目錄下妙黍,還有多個 SocketServer 的實現(xiàn)悴侵,從這些 SocketServer 的名字就可以看出是針對哪些平臺特化實現(xiàn),比如MacCocoaSocketServer废境。在這里我就不對這些 SocketServer 進行分析畜挨,只要能夠明白 PhysicalSocketServer 的原理,再加上對平臺API的了解應(yīng)該很容易讀懂這些平臺特化的 SocketServer噩凹。
在該文件中將大量使用 socket 相關(guān)的系統(tǒng)調(diào)用巴元。由于本篇章的內(nèi)容為多線程編程,所以盡量不做過多的涉及驮宴,并且忽略引用其他文件定義的 socket 相關(guān)類(比如 PhysicalSocket )逮刨。
Dispatcher
該類定義了事件分發(fā)器的純虛基類。事件分發(fā)器主要是將 IO 或 Event 信號對應(yīng)到一定的處理函數(shù)上去堵泽。該純虛基類在 Windows 和 Posix2 個平臺下定義的函數(shù)接口有很大的區(qū)別修己。但是主要的功能大致差不多。
PhysicalSocket
該類是 physicalsocketserver.cc 文件的內(nèi)部私有的類迎罗,不對外暴露睬愤,它主要是對 Socket 的跨平臺封裝。由于 Windows 也提供了基本和 BSDsocket 一致的 Socket API 所以該類的代碼不難理解纹安。
PhysicalSocket 的主要組件包括:
s_:socket句柄/文件描述符
enabled_events_:需要監(jiān)聽的IO事件
udp_:通信方式是否為UDP
error_:最后出錯碼(Last Error)
state_:連接狀態(tài)
resolver_:異步的網(wǎng)址解析器
PhysicalSocket 的主要成員函數(shù)包括:
構(gòu)造函數(shù):創(chuàng)建并初始化PhysicalSocket對象
參數(shù)說明:
ss:管理的SocketServer
s:封裝的系統(tǒng)socket句柄/文件描述符尤辱;如果不提供該參數(shù), PhysicalSocket 將創(chuàng)建一個厢岂;如果提供 PhysicalSocket 將對其進行封裝
PhysicalSocket::Create:創(chuàng)建系統(tǒng) socket 句柄/文件描述符
參數(shù)說明:
family:socket的尋址方案(AF_INET/AF_INET6)光督;從這個參數(shù)可以看出 WebRTC 是支持IPV6的
type:socket的類型(TCP/UDP)
PhysicalSocket::Connect:連接指定的地址和端口,如果地址尚未解析塔粒,調(diào)用異步的地址解析器
參數(shù)說明:
addr:需要連接的地址(使用 SocketAddress结借,該類包括地址和端口號)
PhysicalSocket::EstimateMTU:獲取socket連接的MTU(最大傳輸單元)
參數(shù)說明:
mtu:返回最大傳輸單元
返回值:出錯碼
原理:
Windows 平臺調(diào)用 Ping 庫獲取
IOS 和 OSX 平臺沒有簡單地方法獲取,直接返回出錯
Linux 平臺調(diào)用 getsockopt(s_,IPPROTO_IP, IP_MTU, &value, &vlen)獲取
由于 Windows 提供了基本和 BSD socket一致的 API卒茬,所以在 Windows 的代碼和 Linux 的代碼幾乎一致船老。一下將簡單對比一下 API:
socket咖熟、getsocketname、getpeername努隙、bind球恤、connect、recv荸镊、recvfrom咽斧、accept、closesocket
Linux 平臺下完全一致
getsockopt躬存、setsockopt
Linux 平臺下完全一致(僅有一些特殊的選項不同)
send张惹、sendto
在 Linux 平臺下將最后一個參數(shù) flag 設(shè)置為 MSG_NOSIGNAL,屏蔽 SIGPIPE
PhysicalSocket 需要注意 2 點:
雖然它繼承自 AsyncSocket岭洲,但是它管理的內(nèi)部系統(tǒng) socket 句柄/描述符是阻塞的(它的子類 SocketDispatcher 會將系統(tǒng) socket 句柄/描述符轉(zhuǎn)為非阻塞的)宛逗。繼承自 AsyncSocket 的原因可能是為了避免多繼承造成類繼承結(jié)構(gòu)過于復雜。
雖然該類具有很多和 SocketServer 相關(guān)的成員變量和成員函數(shù)盾剩,但是 PhysicalSocketServer 不能直接管理PhysicalSocket雷激。能被 PhysicalSocketServer 管理的是 PhysicalSocket 的一個子類 SocketDispatcher,它也是一個 physicalsocketserver.cc 內(nèi)部私有的一個類告私。
當然以上這 2 點只是實現(xiàn)的細節(jié)問題屎暇,這些類都是不對外暴露的,我們僅僅需要懂得如何使用對外暴露的接口就可以了驻粟。
EventDispatcher
EventDispatcher 類實現(xiàn)了跨平臺的等價于 Win32 自動重置( autoreset )WSAEvent 的功能根悼。在 Windows 平臺上有網(wǎng)絡(luò)多線程開發(fā)經(jīng)驗的讀者應(yīng)該很熟悉 WSAEvent,我就不再多做介紹了蜀撑。
之前講解過 Event 類挤巡。那 PhysicalSocketServer 還需要 EventDispatcher 來模擬 Event 呢?這是因為 Event 僅僅實現(xiàn)了 Win32 的 WaitForSingleObject 函數(shù)的功能酷麦,這無法應(yīng)用到多路信號分離器里面去矿卑。多路信號分離器需要有能力在一個阻塞函數(shù)里等待多個 event 和 IO 信號的能力,在 Windows 平臺上就是W SAWaitForMultipleEvents 函數(shù)的功能沃饶。
與之相類似的在 Linux 平臺上可以使用的 API 有 select 函數(shù)粪摘,不過 select 函數(shù)只能等待 IO 信號不能等待其他 Event。這就很難實現(xiàn)一個阻塞函數(shù)同時等待 IO 信號和消息隊列的功能绍坝。這對于網(wǎng)絡(luò)服務(wù)器端的開發(fā)并不是非常重要,但是對于圖形用戶界面客戶端的開發(fā)沒有這個功能有時會變得很麻煩苔悦。而 EventDispatcher 的職責就是為我們模擬出這個功能轩褐。
既然 select 函數(shù)已經(jīng)具有了我們需要的一半功能,怎樣才能獲得另一半功能呢玖详?比較直接的方法就是將一個 event 的 signal 語義轉(zhuǎn)化為 IO 信號把介,是的 WebRTC 就是這么做的勤讽。linux 版本的 EventDispatcher 的一個成員變量是一對管道( pipe )的文件描述符。如果需要 signal 一個 EventDispatcher拗踢,只要對這個管道發(fā)送一個字節(jié)(內(nèi)容無所謂)脚牍,就能打開 select 函數(shù)的阻塞狀態(tài)。緊接著 EventDispatcher 會立即將管道內(nèi)的數(shù)據(jù)讀出巢墅,EventDispatcher 重新回到unsignal 的狀態(tài)诸狭。這就實現(xiàn)了 event 的 auto reset 語義。當然君纫,這種實現(xiàn)并不完美驯遇,比如說多個 select 等待在同一個EventDispatcher 上就會出問題。WebRTC 的開發(fā)人員也明白這一點蓄髓,所以對 EventDispatcher 加上了以下注釋:
[cpp] view plain copy
// It is not possible to perfectly emulate an auto-resetting event with
// pipes. This simulates it byresetting before the event is handled.
雖然存在以上的問題叉庐,但這并不影響 EventDispatcher 配合 PhysicalSocketServer 的工作。因為每一個EventDispatcher 實例僅隸屬于一個 PhysicalSocketServer会喝,所以不存在多個 PhysicalSocketServer 等待同一個EventDispatcher 的狀況陡叠。這也可能是為什么 EventDispatcher 是 physicalsocketserver.cc 文件私有的原因。
通過上面的講解我們應(yīng)該可以理解 Linux 版的 EventDispatcher 的工作原理肢执。以下我將 Windows 版本和 Linux 版本的API 調(diào)用做一下簡單類比:
WSACreateEvent
pipe函數(shù)枉阵,用于創(chuàng)建管道
WSACloseEvent
close函數(shù),用于關(guān)閉管道
WSASetEvent
write函數(shù)蔚万,向管道內(nèi)寫入一個字節(jié)數(shù)據(jù)岭妖,用以解鎖阻塞的 select 函數(shù)
WSAResetEvent
read 函數(shù),從管道內(nèi)讀出所有數(shù)據(jù)反璃,清除管道的可讀狀態(tài)昵慌,下次調(diào)用 select 函數(shù)時恢復阻塞
PosixSignalHandler
PosixSignalHandler 和 PosixSignalDispatcher 這兩個類只有 Linux 版本。并且在整個 WebRTC 的源代碼中沒有任何地方使用過這兩個類淮蜈。因此斋攀,對它們的代碼分析主要是為了幫助 Windows 開發(fā)人員從實用的角度學習如何使用 linux 平臺下的部件。
PosixSignalHandler 類主要實現(xiàn)了將 Linux 的 Signal 機制納入到多路分離器的架構(gòu)中去梧田。對于 Windows 開發(fā)人員來說淳蔼,Signal 機制是一個比較陌生的東西。而且對它的處理比較麻煩裁眯。因為 Signal 會在程序運行的任何時候出現(xiàn)鹉梨,一旦觸發(fā)就會調(diào)用注冊的處理函數(shù),開發(fā)人員沒法假定這時程序中哪些工具是否可用穿稳。正如在PosixSignalHandler::OnPosixSignalReceived 函數(shù)的注釋中所說的在出錯的時候我們甚至無法記錄log:
[cpp] view plain copy
// Nothing we can do here. If there's an error somehow then there's
// nothing we can safely do from a signal handler.
// No, we can't even safely log it.
此外存皂,Linux 的有些 signal 相關(guān)的函數(shù)在不同版本的 Linux/Unix 平臺以及的表現(xiàn)完全不同。因為,signal 機制是一個古老的歷史遺留問題旦袋,在當年 Unix 主導一切骤菠,各個大公司又各自為戰(zhàn)的年代,要提供一個公認完備的標準確實不易疤孕。幸運的是從WebRTC 的代碼來看商乎,幾乎沒有什么組件使用了 signal 機制,那就說明絕大多數(shù)的現(xiàn)代程序都是可以不使用 signal 機制就能實現(xiàn)自己想要的功能的祭阀。
首先鹉戚,讓我們來看一下 PosixSignalHandler 類的工作原理。它被偽裝成了一個 singleton柬讨,而它其實是一個全局唯一的對象崩瓤,創(chuàng)建后永不釋放,直到程序退出時內(nèi)存泄露踩官。注意却桶,這不是開發(fā)人員不小心泄露了內(nèi)存,而是主動的泄露了內(nèi)存蔗牡。在 WebRTC 中有一個專用的宏( LIBJINGLE_DEFINE_STATIC_LOCAL )用來定義定義這種類實例颖系。如果,在程序中有限的幾個類實例被設(shè)置成 LIBJINGLE_DEFINE_STATIC_LOCAL 理論上來說是沒有什么負面效果的辩越。因為它不會造成程序在運行期間不斷地積累內(nèi)存泄露直到拖垮整個系統(tǒng)嘁扼。
但是使用這種手法依然需要謹慎。在使用 PosixSignalHandler 的時候黔攒,PhysicalSocketServer 會將 PosixSignalHandler::OnPosixSignalReceived 函數(shù)通過 sigaction 注冊到系統(tǒng)以響應(yīng)感興趣 signal趁啸。當系統(tǒng)發(fā)出被監(jiān)聽的 signal 后 OnPosixSignalReceived 函數(shù)會被調(diào)用。該函數(shù)會在成員變量 received_signal_(數(shù)組)中相應(yīng)的位置上設(shè)置為 true督惰,并在 pipe 中寫入 1 個字節(jié)的數(shù)據(jù)(是的不傅,它的核心工作原理和 EventDispatcher 是一樣的),這樣就能解鎖阻塞等待在 select 函數(shù)上的 PhysicalSocketServer赏胚。PhysicalSocketServer 通過PosixSignalHandler::IsSignalSet 函數(shù)來檢查 received_signal_ 數(shù)組以確定哪個 signal 被激活访娶,并調(diào)用相應(yīng)的處理函數(shù)。
在PosixSignalHandler中主要使用的Linux API包括:
pipe:創(chuàng)建管道
fcntl:設(shè)置文件描述符的選項觉阅;該函數(shù)在構(gòu)造函數(shù)中被調(diào)用崖疤,將新創(chuàng)建的一對pipe文件描述符設(shè)置為非阻塞。調(diào)用代碼為:fcntl(afd_[0], F_SETFL, O_NONBLOCK)典勇。不過劫哼,根據(jù)EventDispatcher構(gòu)造函數(shù)的代碼來看,這一步好像沒有必要割笙÷儋耍考慮到EventDispatcher是一個被重度使用的對象,可以確信沒有必要把pipe的文件描述符設(shè)置為非阻塞(pipe默認應(yīng)該就是阻塞的,并且在絕大多數(shù)系統(tǒng)中IO默認都是阻塞的)豪嚎。
read:從管道讀出數(shù)據(jù),解除管道文件描述符的可讀狀態(tài)(該函數(shù)在PosixSignalDispatcher中使用)
write:向管道寫入數(shù)據(jù)谈火,以解鎖阻塞的select函數(shù)
close:關(guān)閉管道
sigaction:將signal處理函數(shù)注冊到系統(tǒng)侈询,當signum指定編號的signal觸發(fā)時,系統(tǒng)會調(diào)用相應(yīng)的處理函數(shù)(該函數(shù)在PhysicalSocketServer::InstallSignal中使用)
PosixSignalDispatcher
該類也是 Linux 獨有的一個類糯耍,主要是作為代表 PosixSignalHandler 的分發(fā)器扔字,通過將該對象添加入PhysicalSocketServer 可以將實現(xiàn)接收 PosixSignal。它的原理已經(jīng)在上一節(jié) PosixSignalHandler 中討論過了温技,這里就不再多做分析革为。它的主要函數(shù)如下:
PosixSignalDispatcher::SetHandler:將signal的響應(yīng)函數(shù)加入到分發(fā)器
參數(shù)說明:
signum:需要響應(yīng)的signal編號
handler:當signal觸發(fā)時,響應(yīng)的處理函數(shù)
SocketDispatcher
SocketDispatcher 類主要將 PhysicalSocket 封裝成一個分發(fā)器舵鳞。所以震檩,在實現(xiàn)上該類僅僅就是為 PhysicalSocket 添加一些 Dispatcher 接口需要的一些成員函數(shù),以及一些狀態(tài)維護代碼蜓堕。僅有在 Linux 版本僅有的成員函數(shù)SocketDispatcher::IsDescriptorClosed 中有些比較特殊的情況抛虏。通過這個函數(shù)中的注釋我們發(fā)現(xiàn)尚無一些可靠的手段判斷一個 socket 文件描述符是否已經(jīng)被關(guān)閉,所以實現(xiàn)代碼使用了 ::recv(s_, &ch, 1, MSG_PEEK) 來判斷套才。在 Window 版本的 SocketDispatcher 中沒有這個成員函數(shù)迂猴。除此之外,SocketDispatcher 類的 Windows 實現(xiàn)和 Linux 實現(xiàn)基本一致背伴。以下對比一下 2 個平臺 API 調(diào)用的情況:
ioctlsocket
fcntl函數(shù)用于設(shè)置文件描述符的選項(比如阻塞或非阻塞)
FileDispatcher
FileDispatcher 類是一個 Linux 平臺獨有的類沸毁,它的功能是簡單將文件描述符封裝成 Dispatcher。不過它在 WebRTC 中基本上沒有使用過傻寂。該類在創(chuàng)建的時候(構(gòu)造函數(shù)中)抽碌,接受并保存文件描述符,并通過 fcntl 函數(shù)將文件描述符設(shè)置為非阻塞荚孵。此外它還實現(xiàn)了一些 Dispatcher 接口要求的一些函數(shù)较剃,并維護一些狀態(tài)變量。該類并沒有什么難點个绍。
Signaler
Signaler 的用處只有一個:實現(xiàn) PhysicalSocketServer 的 signal_wakeup_ 成員變量(用于解除多路信號分離器的阻塞狀態(tài))勒葱。它的主要功能由父類 EventDispatcher 實現(xiàn),僅僅添加了在解除多路信號分離器阻塞狀態(tài)后將PhysicalSocketServer::fwait_ 成員變量設(shè)置為 false 的代碼巴柿。PhysicalSocketServer::fwait_ 成員變量在被設(shè)置為 false 后凛虽,PhysicalSocketServer::Wait 函數(shù)就會退出,MessageQueue 就可以及時處理消息隊列的消息广恢。
PhysicalSocketServer
如果說 MessageQueue 是多路信號分離器的外圍凯旋,那么 PhysicalSocketServer 就是多路信號分離器的真正核心。PhysicalSocketServer 主要實現(xiàn)了消息和 IO 的多路分發(fā)功能,類似于 Windows 平臺上的 WSAWaitForMultipleEvents 的功能至非。
PhysicalSocketServer 的主要成員變量包括:
dispatchers_:分發(fā)器列表
signal_wakeup_:中止 PhysicalSocketServer::Wait 函數(shù)的 Signaler 對象(通常在 MessageQueue 接收到事件時調(diào)用)
PhysicalSocketServer 的主要成員函數(shù)包括:
PhysicalSocketServer::CreateSocket:創(chuàng)建一個Socket實例钠署,實質(zhì)為PhysicalSocket
參數(shù)說明:
family:socket 的尋址方案(AF_INET/AF_INET6),說明 WebRTC 能夠支持 IPv6
type:socket 的類型(TCP/IP)
PhysicalSocketServer::CreateAsyncSocket:創(chuàng)建一個AsyncSocket實例荒椭,實質(zhì)是SocketDispatcher谐鼎。此外,與PhysicalSocketServer::CreateSocket函數(shù)不同的是創(chuàng)建后的實例立即被添加入PhysicalSocketServer的分發(fā)器列表(dispatchers_)趣惠。用戶不需要在調(diào)用PhysicalSocketServer::Add函數(shù)
參數(shù)說明:
family:socket的尋址方案(AF_INET/AF_INET6)狸棍,說明WebRTC能夠支持IPv6
type:socket的類型(TCP/IP)
PhysicalSocketServer::WrapSocket:將一個系統(tǒng)socket句柄/文件描述符封裝成SocketDispatcher并添加入分發(fā)器列表(PhysicalSocketServer::dispatchers_)
參數(shù)說明:
s:一個系統(tǒng)socket句柄/文件描述符,可以是同步(阻塞)的味悄,也可以是異步(非阻塞)的草戈。如果是同步的,該函數(shù)會通過fcntl(Linux)或ioctlsocket(Windows)轉(zhuǎn)成異步的侍瑟。
PhysicalSocketServer::Add/Remove:向分發(fā)器列表添加/刪除一個分發(fā)器
參數(shù)說明:
pdispatcher:一個添加/刪除的Dispatcher實例唐片,PhysicalSocketServer在下一次IO監(jiān)聽循環(huán)中會添加/刪除監(jiān)聽它的句柄/文件描述符。注意丢习,是下一次監(jiān)聽循環(huán)牵触,新添加的分發(fā)器不會影響當前阻塞的select(linux)函數(shù)或WSAWaitForMultipleEvents(Windows)函數(shù)。也不會喚醒當前阻塞的select(Linux)函數(shù)或WSAWaitForMultipleEvents(Windows)函數(shù)咐低。如果開發(fā)人員需要添加/刪除操作立即生效揽思,需要自行喚醒當前阻塞的select(Linux)函數(shù)或WSAWaitForMultipleEvents(Windows)函數(shù)。
PhysicalSocketServer::Wait:實現(xiàn)了多路信號分離器
參數(shù)說明:
cmsWait:以毫秒為單位的等待時間见擦,kForever表示永久等待
process_io:是否處理IO
PhysicalSocketServer 的核心代碼就在成員函數(shù) Wait 中钉汗。該函數(shù)比較復雜我將分幾個要點來講解:
大體流程
在 PhysicalSocketServer::Wait 函數(shù)中,代碼的主體為 IO 監(jiān)聽循環(huán)鲤屡。在 IO 監(jiān)聽循環(huán)中损痰,Wait 函數(shù)首先將分發(fā)器列表( PhysicalSocketServer::dispatchers_ )中所有分發(fā)器的 IO 句柄/文件描述符加入到監(jiān)聽數(shù)組(需要注意的是分發(fā)器列表已經(jīng)包括了 PhysicalSocketServer::signal_wakeup_,激發(fā)該分發(fā)器可以終止整個 IO 監(jiān)聽循環(huán)酒来,導致 Wait 函數(shù)退出)卢未。接著 Wait 函數(shù)就調(diào)用系統(tǒng)的 IO 阻塞等待函數(shù),在 Linux 平臺上為 select堰汉,在 Windows 平臺上為 WSAWaitForMultipleEvents辽社。
Wait 函數(shù)阻塞等待 IO 期間釋放 CPU 資源。在阻塞等待 API 返回時翘鸭,Wait 函數(shù)先檢查它的返回值滴铅。如果是因為等待超時,Wait 函數(shù)將立即返回就乓。否則汉匙,將調(diào)用被激發(fā) IO 句柄/文件描述符的分發(fā)器的 OnPreEvent 和 OnEvent 函數(shù)拱烁。最后,檢查 PhysicalSocketServer::fwait_噩翠,如果需要繼續(xù)等待就再次執(zhí)行 IO 監(jiān)聽循環(huán)戏自,否則就退出 Wait 函數(shù)。整個函數(shù)流程如下圖所示:
阻塞等待機制(Windows)
雖然 PhysicalSocketServer::Wait 函數(shù)在 Windows 平臺和 Linux 平臺上的流程大體相同绎秒,但是在實現(xiàn)細節(jié)上卻有很大不同浦妄。首先讓我們先看一下 Windows 平臺。Windows 平臺調(diào)用的等待 API 是 WSAWaitForMultipleEvents见芹,該函數(shù)有能力將多個 IO 句柄使用 WSAEventSelect 函數(shù)綁定到一個 WSAEvent 句柄上去,并在等待結(jié)束后調(diào)用 WSAEnumNetworkEvents 來確定到底哪些 IO 句柄被激發(fā)蠢涝。
所以玄呛, Windows 版本的 Dispatcher 定義有兩個成員函數(shù) GetSocket 和 GetWSAEvent。如果能調(diào)用 GetSocket 函數(shù)返回一個有效的 socket和二,那么就將這個 socket 句柄綁定到一個統(tǒng)一的 WSAEvent 上徘铝;如果不能返回一個有效的 socket 句柄就繼續(xù)調(diào)用 GetWSAEvent,取出分發(fā)器的 WSAEvent惯吕,并把它加入到 WSAWaitForMultipleEvents 函數(shù)的等待數(shù)組中去惕它。大致過程如下圖所示:
阻塞等待機制(Linux)
Linux 的 PhysicalSocketServer::Wait 相對來說比較簡單。它使用 select 函數(shù)等待所有從Dispatcher::GetDescriptor 返回的文件描述符废登。所有的文件描述符一視同仁淹魄,也沒有內(nèi)置特殊文件描述符。select 函數(shù)返回后調(diào)用相應(yīng)的 Dispatcher 的事件響應(yīng)函數(shù) OnPreEvent 和 OnEvent堡距。唯一比較復雜的就是 PhysicalSocketServer::signal_dispatcher_甲锡,具體的原理見 PosixSignalHandler。
與 MessageQueue 互動
其實羽戒,這部分內(nèi)容已經(jīng)在前面的章節(jié)講述過一些了缤沦,只是比較分散。在這里我將比較全面總結(jié)一下:
整個多路信號分離器由 MessageQueue 和 PhysicalSocketServer 組成易稠,這 2 個組件輪流獲得控制權(quán)缸废。 MessageQueue 最先獲得控制權(quán),它會檢查自己的消息隊列驶社,如果有需要立即處理的消息就馬上處理企量,如果沒有就把控制權(quán)交給 PhysicalSocketServer。PhysicalSocketServer 將等待所有位于其分發(fā)器列表(PhysicalSocketServer::dispatchers_)的 IO 句柄/文件描述符衬吆。如果有 IO 句柄/文件描述符被激發(fā)梁钾,PhysicalSocketServer 將調(diào)用對應(yīng)的 Dispatcher 的消息響應(yīng)函數(shù)(OnPreEvent、OnEvent)逊抡。
如果在 PhysicalSocketServer 阻塞等待時 MessageQueue 接收到消息姆泻,MessageQueue 將會調(diào)用PhysicalSocketServer::WakeUp 函數(shù)激發(fā) PhysicalSocketServer::signal_wakeup_ 以解除PhysicalSocketServer 的阻塞狀態(tài)零酪。并將 PhysicalSocketServer::fWait_ 設(shè)置為false,這將導致PhysicalSocketServer 退出 IO 監(jiān)控循環(huán)重新將控制權(quán)交給 MessageQueue拇勃。MessageQueue 獲得控制權(quán)后將立即處理消息四苇,在完成消息處理后再將控制權(quán)交給 PhysicalSocketServer。
由于方咆,PhysicalSocketServer 的實現(xiàn)比較復雜月腋,因此已經(jīng)無法比較 Windows 平臺和 Linux 平臺的代碼。所以瓣赂,僅僅簡單羅列一下Linux平臺下的API:
select:用于對IO文件描述符數(shù)組進行輪詢榆骚,阻塞等待IO信號
FD_ZERO:用于初始化一個IO文件描述符數(shù)組的宏
FD_SET:用于將IO文件描述符添加入由FD_ZERO初始化的IO文件描述符數(shù)組的宏
FD_ISSET:用于檢查一個IO文件描述符數(shù)組是否包括指定的IO文件描述符。由于select函數(shù)在返回時會將沒有激發(fā)的IO文件描述符剔除掉煌集,所以依然存在于數(shù)組中的IO文件描述符表示已經(jīng)被激發(fā)
FD_CLR:從IO文件描述符數(shù)組中刪除一個指定的IO文件描述符
到此妓肢,我們已經(jīng)完成了對整個多路信號分離器的分析。但是苫纤,這還不是 WebRTC 線程模型的全部碉钠,它還有一個重要的模塊——thread,之后將對它進行分析卷拘。