前面分析了跟TaskScheduler相关的3个链表,那么现在就剩下最后一个doEventLoop函数了。
在BasicTaskScheduler0中对doEventLoop进行了实现,它是通过循环调用 SingleStep 来完成功能的。
在这里需要提一下为什么要通过 while(1) 这个循环来不停的调用SingleStep,因为在
SingleStep中 有 select 函数,对于单线程 网络通讯 select 模型,必须要不停的循环调用select函数(同步阻塞模式),因为select是监控某些端口,如果这个端口 可读、可写或者有带外数据 select就返回,所以如果我们要在某些端口通讯 那么就一定要循环调用select 。
在SingleStep里面首先是:
fd_set readSet = fReadSet;....等3个表达式,这是创建3个fd_set副本(分别表示可读、可写、有带外数据),
每个副本包含了需要监控的socket,所以把这3个fd_set传给select函数就可以监控 对应的socket状态了。
这3个表达式后面是一段对 select等待时间的处理,我们可以无视这些语句。直接到select函数这里来。
int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);在Windows下面第一个参数可以无视,2~4这3个参数就是刚才提到的3个fd_set结构体用于监控对应的socket的3种状态,注意每次select返回后fd_set也会发生相应的变化,所以前面是创建副本作为参数而不是直接把自己作为参数,第5个参数就是阻塞时间了,也就是select最长的等待时间,如果有状态改变就返回,如果超时就返回0,否知返回 -1.所以后面 if (selectResult < 0) 这块代码是对错误的处理,我们也可以无视。直接到HandlerIterator iter(*fHandlers);fHandlers就是前面提到的与BackgroundHandling相关的链表。iter 是与之对应的迭代器(。。。这里抱怨下,这个迭代器跟STL里面比起来还是不行啊,明显没STL专业)。iter这个时候指向fHandlers的第一个节点。下面就是一段对1个状态改变的socket 进行相关的处理来,具体分析如下:首先判断fLastHandledSocketNum是否大于零,也就是在之前是否有过对应的节点处理,如果有那说么找到这个节点:if (handler->socketNum == fLastHandledSocketNum) break;这个时候已经找到前面处理过的那个节点了,然后handler = iter.next() 来让handler指向下一个节点(当然我们假设下一个节点不为空)接着通过 FD_ISSET 来判断这个节点对应的 socket 的状态是否发生改变,如果发生改变 那么调用(*handler->handlerProc)(handler->clientData, resultConditionSet);然后break;当然如果fLastHandledSocketNum小于零,那么就从第一个节点开始依次来判断哪个节点的socket状态发生变化然后调用相关的函数来处理。所以以上这些代码其实目的就是处理1个状态发生改变的socket。注意是1个。(但是select不一定只监控到1个状态发生变化)在这之后就是一段跟 我们前面说的第二个链表 相关的代码。EventTrigger.:fTriggersAwaitingHandling 这是一个掩码(每个标识位上对应等待触发事件的标识符),这里程序只是触发了最高的一个标识位((*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);),然后把这个标识位置0( fTriggersAwaitingHandling &=~ mask;).EventTrigger之后就是我们前面提到的第三个链表DelayedQueue:fDelayQueue.handleAlarm();这句语句是用来处理 第一个超时的节点。所以总结如下:【SingleStep】1.首先是select 函数 等待 网络数据的到来,或者我们有需要发送的数据。select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);2.如果是找到 1 个需要通讯的 socket,进行相关的处理(处理函数和数据都在对应的节点里面保存了的)。(*handler->handlerProc)(handler->clientData, resultConditionSet);3.如果有等待 触发的事件 那么就 触发标识位最高的那个 事件(*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);4.处理第一个超时的 节点fDelayQueue.handleAlarm();好了,经过几天的分析对usageEnvironment,TaskScheduler以及BasicUsageEnvrionment和BasicTaskScheduler结构有了比较清晰的了解,后面有时间对先分析一下groupsock 库,最后再看liveMedia,先易后难~~,不知道后面能不能坚持下去,不过至少最近是可以的,因为暂时 思路还算清晰,后面内容多了 就难说了~~
TaskScheduler工作原理剖析
本文详细解析了TaskScheduler中doEventLoop函数的工作流程,包括如何使用select监控socket状态变化、处理socket事件、触发事件和处理超时任务。通过分析SingleStep函数内部实现,帮助读者理解网络通讯中的关键环节。
5988

被折叠的 条评论
为什么被折叠?



