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

来源:互联网 发布:数据库流程图怎么画 编辑:程序博客网 时间:2024/05/22 12:59

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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 合同上不写工资怎么办 已经上班了想考个大专文凭怎么办 微信小程序违规暂停服务怎么办 程序锁密码忘了怎么办 忘了应用锁密码怎么办 毕业后发现论文有错误怎么办 柯丽尔打胶片时胶片卡住了怎么办 闽教英语要收费怎么办 高一学生英语差怎么办 高一英语基础差怎么办 老师教育学生学生跑出教室怎么办 研究生课题难出论文怎么办 一审过了上诉期怎么办 民事判决赔偿不给钱怎么办 民事申诉期过了怎么办 过了两年申诉期怎么办 微快递下单不能定位怎么办 网上打字兼职被骗了怎么办 微信银行卡转错怎么办 在支付宝被诈骗怎么办 发现被骗了报警不理怎么办 投稿投错了网站怎么办 六个月宝宝吃辅食便秘怎么办 六个月宝宝加辅食后便秘怎么办 婴儿6个月便秘怎么办 7个月的孩子便秘怎么办 四个月宝宝喜欢吃手怎么办 博瑞智教育是上当了怎么办 我43岁记忆力差怎么办 艾灸灸出的湿疹怎么办 饭店合同到期房东不租怎么办 极端暴力控制不住自己怎么办 苹果已停止访问该网页怎么办 qq登陆后隐藏了怎么办 易班密码忘记了怎么办 老师上课讲错了怎么办 专升本差了一分怎么办 登录不上学信网怎么办 steam被好友删了怎么办 护士继续教育学分证丢了怎么办 护士证到期未延续注册怎么办