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

来源:互联网 发布:js延时函数 编辑:程序博客网 时间:2024/05/18 15:57

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,我们将在下一节对它进行分析。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 coolpad酷派手机开不了机怎么办 苹果5s黑屏开不了机怎么办 苹果4s的屏坏了怎么办 苹果6手机充电口接触不良怎么办 5s用久了卡顿怎么办 孕妇血糖高怎么办什么方法降最好 脚砸了肿了紫了怎么办 我想在淘宝上卖东西该怎么办 苹果手机4s开不了机怎么办 冒险岛s前出2条怎么办 狗狗又吐又拉血怎么办 小孩上网成瘾怎么办父母要怎么做 一只眼睛大一只眼睛小怎么办 带近视镜时间长了眼睛变形怎么办 联通卡2g换4g卡怎么办 上火牙疼怎么办教你立刻止疼 吃热的凉的牙疼怎么办 我买的股票退市了怎么办 如果起诉离婚另一方不出庭怎么办 10个月宝宝还没长牙怎么办 超敏c反应蛋白>5怎么办 怀孕才两个月肚子就大了怎么办 腰椎间盘突出腿疼厉害怎么办 打了促排卵针不排卵怎么办 孕34周隐血1十是怎么办 窦性心动过缓伴不齐怎么办 09年买的万科b怎么办 苹果5s手机打不开机怎么办 剖腹产后一年半后意外怀孕怎么办 考驾照挂了5次了怎么办 怀孕咳嗽一个月了好不了怎么办 孕妇餐后2小时血糖高怎么办 我想开网店但我不懂该怎么办 新开的淘宝店没生意怎么办 做肝胆b超喝了水怎么办 红米1s刷机失败怎么办 红米3x手机太卡怎么办 红米2a线刷失败怎么办 红米2用不了4g怎么办 小米红米3s卡顿怎么办 魅蓝4g网速很慢怎么办