WebRTC源代码探索之旅——多线程篇(5 - 4)

5.9 talk_base::PhysicalSocketServer

 

如果说talk_base::MessageQueue是多路信号分离器的外围,那么talk_base::PhysicalSocketServer就是多路信号分离器的真正核心。talk_base::PhysicalSocketServer主要实现了消息和IO的多路分发功能,类似于Windows平台上的WSAWaitForMultipleEvents的功能。

 

talk_base::PhysicalSocketServer的主要成员变量包括:

dispatchers_:分发器列表

signal_wakeup_:中止talk_base::PhysicalSocketServer::Wait函数的talk_base::Signaler对象(通常在talk_base::MessageQueue接收到事件时调用)

 

talk_base::PhysicalSocketServer的主要成员函数包括:

talk_base::PhysicalSocketServer::CreateSocket:创建一个talk_base::Socket实例,实质为talk_base::PhysicalSocket

参数说明:

family:socket的寻址方案(AF_INET/AF_INET6),说明WebRTC能够支持IPv6

type:socket的类型(TCP/IP)

 

talk_base::PhysicalSocketServer::CreateAsyncSocket:创建一个talk_base::AsyncSocket实例,实质是talk_base::SocketDispatcher。此外,与talk_base::PhysicalSocketServer::CreateSocket函数不同的是创建后的实例立即被添加入talk_base::PhysicalSocketServer的分发器列表(dispatchers_)。用户不需要在调用talk_base::PhysicalSocketServer::Add函数

参数说明:

family:socket的寻址方案(AF_INET/AF_INET6),说明WebRTC能够支持IPv6

type:socket的类型(TCP/IP)

 

talk_base::PhysicalSocketServer::WrapSocket:将一个系统socket句柄/文件描述符封装成talk_base::SocketDispatcher并添加入分发器列表(talk_base::PhysicalSocketServer::dispatchers_)

参数说明:

s:一个系统socket句柄/文件描述符,可以是同步(阻塞)的,也可以是异步(非阻塞)的。如果是同步的,该函数会通过fcntl(Linux)或ioctlsocket(Windows)转成异步的。

 

talk_base::PhysicalSocketServer::Add/Remove:向分发器列表添加/删除一个分发器

参数说明:

pdispatcher:一个添加/删除的talk_base::Dispatcher实例,talk_base::PhysicalSocketServer在下一次IO监听循环中会添加/删除监听它的句柄/文件描述符。注意,是下一次监听循环,新添加的分发器不会影响当前阻塞的select(Linux)函数或WSAWaitForMultipleEvents(Windows)函数。也不会唤醒当前阻塞的select(Linux)函数或WSAWaitForMultipleEvents(Windows)函数。如果开发人员需要添加/删除操作立即生效,需要自行唤醒当前阻塞的select(Linux)函数或WSAWaitForMultipleEvents(Windows)函数。

 

talk_base::PhysicalSocketServer::Wait:实现了多路信号分离器

参数说明:

cmsWait:以毫秒为单位的等待时间,kForever表示永久等待

process_io:是否处理IO

 

talk_base::PhysicalSocketServer的核心代码就在成员函数Wait中。该函数比较复杂我将分几个要点来讲解:

a. 大体流程

在talk_base::PhysicalSocketServer::Wait函数中,代码的主体为IO监听循环。在IO监听循环中,Wait函数首先将分发器列表(talk_base::PhysicalSocketServer::dispatchers_)中所有分发器的IO句柄/文件描述符加入到监听数组(需要注意的是分发器列表已经包括了talk_base::PhysicalSocketServer::signal_wakeup_,激发该分发器可以终止整个IO监听循环,导致Wait函数退出)。接着Wait函数就调用系统的IO阻塞等待函数,在Linux平台上为select,在Windows平台上为WSAWaitForMultipleEvents。Wait函数阻塞等待IO期间释放CPU资源。在阻塞等待API返回时,Wait函数先检查它的返回值。如果是因为等待超时,Wait函数将立即返回。否则,将调用被激发IO句柄/文件描述符的分发器的OnPreEvent和OnEvent函数。最后,检查talk_base::PhysicalSocketServer::fwait_,如果需要继续等待就再次执行IO监听循环,否则就退出Wait函数。整个函数流程如下图所示:

 

 

b. 阻塞等待机制(Windows)

虽然talk_base::PhysicalSocketServer::Wait函数在Windows平台和Linux平台上的流程大体相同,但是在实现细节上却有很大不同。首先让我们先看一下Windows平台。Windows平台调用的等待API是WSAWaitForMultipleEvents,该函数有能力将多个IO句柄使用WSAEventSelect函数绑定到一个WSAEvent句柄上去,并在等待结束后调用WSAEnumNetworkEvents来确定到底哪些IO句柄被激发。所以,Windows版本的talk_base::Dispatcher定义有两个成员函数GetSocket和GetWSAEvent。如果能调用GetSocket函数返回一个有效的socket,那么就将这个socket句柄绑定到一个统一的WSAEvent上;如果不能返回一个有效的socket句柄就继续调用GetWSAEvent,取出分发器的WSAEvent,并把它加入到WSAWaitForMultipleEvents函数的等待数组中去。大致过程如下图所示:

 


 

c. 阻塞等待机制(Linux)

Linux的talk_base::PhysicalSocketServer::Wait相对来说比较简单。它使用select函数等待所有从talk_base::Dispatcher::GetDescriptor返回的文件描述符。所有的文件描述符一视同仁,也没有内置特殊文件描述符。select函数返回后调用相应的talk_base::Dispatcher的事件响应函数OnPreEvent和OnEvent。唯一比较复杂的就是talk_base::PhysicalSocketServer::signal_dispatcher_,具体的原理见5.4节talk_base::PosixSignalHandler。

 

d. 与talk_base::MessageQueue互动

其实,这部分内容已经在前面的章节讲述过一些了,只是比较分散。在这里我将比较全面总结一下:

i.整个多路信号分离器由talk_base::MessageQueue和talk_base::PhysicalSocketServer组成,这2个组件轮流获得控制权。talk_base::MessageQueue最先获得控制权,它会检查自己的消息队列,如果有需要立即处理的消息就马上处理,如果没有就把控制权交给talk_base::PhysicalSocketServer。talk_base::PhysicalSocketServer将等待所有位于其分发器列表(talk_base::PhysicalSocketServer::dispatchers_)的IO句柄/文件描述符。如果有IO句柄/文件描述符被激发,talk_base::PhysicalSocketServer将调用对应的talk_base::Dispatcher的消息响应函数(OnPreEvent、OnEvent)。

ii. 如果在talk_base::PhysicalSocketServer阻塞等待时talk_base::MessageQueue接收到消息,talk_base::MessageQueue将会调用talk_base::PhysicalSocketServer::WakeUp函数激发talk_base::PhysicalSocketServer::signal_wakeup_以解除talk_base::PhysicalSocketServer的阻塞状态。并将talk_base::PhysicalSocketServer::fWait_设置为false,这将导致talk_base::PhysicalSocketServer退出IO监控循环重新将控制权交给talk_base::MessageQueue。talk_base::MessageQueue获得控制权后将立即处理消息,在完成消息处理后再将控制权交给talk_base::PhysicalSocketServer。

 

由于,talk_base::PhysicalSocketServer的实现比较复杂,因此已经无法比较Windows平台和Linux平台的代码。所以,仅仅简单罗列一下Linux平台下的API:

select:用于对IO文件描述符数组进行轮询,阻塞等待IO信号

FD_ZERO:用于初始化一个IO文件描述符数组的宏

FD_SET:用于将IO文件描述符添加入由FD_ZERO初始化的IO文件描述符数组的宏

FD_ISSET:用于检查一个IO文件描述符数组是否包括指定的IO文件描述符。由于select函数在返回时会将没有激发的IO文件描述符剔除掉,所以依然存在于数组中的IO文件描述符表示已经被激发

FD_CLR:从IO文件描述符数组中删除一个指定的IO文件描述符

 

到此位置,我们已经完成了对整个多路信号分离器的分析。但是,这还不是WebRTC线程模型的全部,它还有一个重要的模块——thread,我们将在下一节对它进行分析。

你可能感兴趣的:(webrtc)