live555 源码分析(五:TaskScheduler分析(下))

这个是最后一个live555的类了,但也是最难的,所以要好好的分析分析。

5.1 BasicTaskScheduler0

5.1.1 BasicTaskScheduler0类

class BasicTaskScheduler0: public TaskScheduler {
public:
  virtual ~BasicTaskScheduler0();

  virtual void SingleStep(unsigned maxDelayTime = 0) = 0;
      // “ maxDelayTime”以微秒为单位。  
      // 它允许子类对“ select()”可以延迟多长时间施加限制,以防它也想进行轮询。
      // 0(默认值)表示:没有最大值; 看看延迟队列

public:
  // Redefined virtual functions:
  virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc,
				void* clientData);
  virtual void unscheduleDelayedTask(TaskToken& prevTask);

  virtual void doEventLoop(char volatile* watchVariable);

  virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc);
  virtual void deleteEventTrigger(EventTriggerId eventTriggerId);
  virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL);

protected:
  BasicTaskScheduler0();

protected:
  // To implement delayed operations:
  DelayQueue fDelayQueue;

  // To implement background reads:
  HandlerSet* fHandlers;
  int fLastHandledSocketNum;

  // To implement event triggers:
  EventTriggerId volatile fTriggersAwaitingHandling; // implemented as a 32-bit bitmap
  EventTriggerId fLastUsedTriggerMask; // implemented as a 32-bit bitmap
  TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS];
  void* fTriggeredEventClientDatas[MAX_NUM_EVENT_TRIGGERS];
  unsigned fLastUsedTriggerNum; // in the range [0,MAX_NUM_EVENT_TRIGGERS)
};

重点成员解释:
fDelayQueue:延时队列

fHandlers
fLastHandledSocketNum

fTriggersAwaitingHandling
fLastUsedTriggerMask
fTriggeredEventHandlers:32个handle函数的数组
fTriggeredEventClientDatas:32个clientData数据
fLastUsedTriggerNum

5.1.2 构造函数

BasicTaskScheduler0::BasicTaskScheduler0()
  : fLastHandledSocketNum(-1), fTriggersAwaitingHandling(0), fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS-1) {
  fHandlers = new HandlerSet;
  for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
    fTriggeredEventHandlers[i] = NULL;
    fTriggeredEventClientDatas[i] = NULL;
  }
}

构造函数还是做一个简单的初始化操作。

5.1.3 延时调度任务

以前感觉写的有点散,以函数为单位写的,这样感觉不好,没有总结一系列的函数,现在加上,把属于这一系列的函数先统一分析一下,然后再到一个个函数。
延时调度任务有3个函数,一个是创建任务,一个是删除任务的,还有一个是重启任务的,不过这里没实现重启任务的函数。

5.1.4 创建延时任务

TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,
						 TaskFunc* proc,
						 void* clientData) {
  if (microseconds < 0) microseconds = 0;
  // 把ms单位转换成DelayInterval 类
  DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
  // 创建一个任务
  AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
  // 添加到等待队列中
  fDelayQueue.addEntry(alarmHandler);
	
  // 返回这个任务的token,这个就是这个任务的标记
  return (void*)(alarmHandler->token());
}

添加延时任务,函数里面的也比较简单,看过上一篇的都不难。

5.1.5 删除延时任务

void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
  DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);
  prevTask = NULL;
  delete alarmHandler;
}

这是删除一个任务,这次看到了删除条目怎么用了,也是使用了token,利用这个token去删除对应的条目,然后返回这个条目的指针,删除的内部是没有做内存释放,所以需要外部手动释放。

5.1.6 用户事件函数

用户事件函数也是一组函数,这组函数有创建任务、删除任务、触发任务。重点还是看看怎么管理这个用户事件,还是先看变量:

  // To implement event triggers:
  EventTriggerId volatile fTriggersAwaitingHandling; // implemented as a 32-bit bitmap
  EventTriggerId fLastUsedTriggerMask; // implemented as a 32-bit bitmap
  TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS];
  void* fTriggeredEventClientDatas[MAX_NUM_EVENT_TRIGGERS];
  unsigned fLastUsedTriggerNum; // in the range [0,MAX_NUM_EVENT_TRIGGERS)

这几个都是用户事件的变量,备注也写了,用bit来表示,EventTriggerId一共32位,所以相当于用32个位存储32个用户事件的状态。
fTriggersAwaitingHandling:表示那些事件已经准备好了,等待触发,也是一个位代表一个事件
fLastUsedTriggerMask:最后一个用户事件的掩码,为了简化,使用的变量
fLastUsedTriggerNum:跟上面对应,这个也是最后一个用户事件的num,为了简化,使用的变量
fTriggeredEventHandlers:存储回调函数的函数指针数组,大小也是32
fTriggeredEventClientDatas:存储用户数据的数组,大小也是32

5.1.7 创建用户事件

EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
  unsigned i = fLastUsedTriggerNum;					// 刚开始时31
  EventTriggerId mask = fLastUsedTriggerMask;		// 刚开始是1

  do {
    i = (i+1)%MAX_NUM_EVENT_TRIGGERS;				// i = (31+1) % 32
    mask >>= 1;										// 右移1    mask =0
    if (mask == 0) mask = 0x80000000;				// mask = 0x80000000

    if (fTriggeredEventHandlers[i] == NULL) {
      // 该触发编号是空闲的,可以使用它:
      fTriggeredEventHandlers[i] = eventHandlerProc;
      fTriggeredEventClientDatas[i] = NULL; // sanity

      fLastUsedTriggerMask = mask;     // 最后一个mask状态
      fLastUsedTriggerNum = i;			// 最后一个num,为了优化做的吧

      return mask;  		//插入成功返回掩码
    }
  } while (i != fLastUsedTriggerNum);				// 31

  // 通过插入函数可以看出,mask=0x80000000 的时候  ,数据填充到fTriggeredEventHandlers[0],这是一个反向对应过程mask最高为对应数组最低位

  // All available event triggers are allocated; return 0 instead:
  return 0;
}

首先看到的就是一个循环判断,因为我们是用位来表示的,所以需要循环判断哪一个位是空闲的,然后进行插入。
其次看到插入mask最高位为1的时候,数组的下标竟然是0,这个是相反的逻辑,不知道为什么这样设计。

5.1.8 删除用户事件

void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
  //把对应的位请0,fTriggersAwaitingHandling的位为1的时候就表示已经事件已就绪
  fTriggersAwaitingHandling &=~ eventTriggerId;				

  if (eventTriggerId == fLastUsedTriggerMask) { // 常见情况优化:这如果只有一个用户事件的话,就可以直接操作
    fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;	// fLastUsedTriggerNum:表示最后一个num,如果上面判断条件成立,是可以这样直接删除
    fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
  } else {
    // “ eventTriggerId”应仅设置一位。
    // 但是,如果用户碰巧将两个或多个“ EventTriggerId”一起“或”在一起,则我们会做合理的事情:
    // 这种方式就是遍历了,想不到还可以这么优化
    EventTriggerId mask = 0x80000000;
    for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
      if ((eventTriggerId&mask) != 0) {
	fTriggeredEventHandlers[i] = NULL;
	fTriggeredEventClientDatas[i] = NULL;
      }
      mask >>= 1;
    }
  }
}

删除用户事件,首先把就绪状态的标记清0,也就是没有就绪了。
其次就是删除的时候做了一个判断,这个判断就是如果只有一个事件的时候,就不需要循环,直接删除,这个是做了优化了,如果有多个事件,没方法了,只能遍历删除了。

5.1.9 触发用户事件

void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
  // 首先,记录“ clientData”。 (请注意,我们允许“ eventTriggerId”为多个事件的位组合。)
  EventTriggerId mask = 0x80000000;
  for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
    if ((eventTriggerId&mask) != 0) {
      fTriggeredEventClientDatas[i] = clientData;
    }
    mask >>= 1;
  }
  
  // 然后,请注意此事件已准备好进行处理。
  //(请注意,由于可以从外部线程调用此函数(与库中的其他函数不同),因此我们最后执行此操作,以减少出现竞争状况的风险。)
  fTriggersAwaitingHandling |= eventTriggerId;
}

这个是触发用户事件;
第一个先遍历,找到合适的位置把用户数据插入;
第二把准备就绪中的对应的位置1。
逻辑很简单,但是我有一点不明白,是不是应该有一个函数来查询这个fTriggersAwaitingHandling变量,然后遍历,看看哪一个为置1,然后拿出对应的函数和数据执行,这个要看到后面才知道。

5.1.10 析构函数

BasicTaskScheduler0::~BasicTaskScheduler0() {
  delete fHandlers;
}

这个简单,直接把这个变量释放了,这个变量,等下会讲。

5.1.11 时间循环框架

void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
  }
}

差点忘记了,这个SingleStep()是虚函数,要让子类实现的,所以这个函数等下看了子类再分析。

5.2 Socket I/O 事件描述及其组织

用了HandlerDescriptor来描述socket事件,HandlerSet类是管理HandlerDescriptor这个类。

5.2.1 HandlerDescriptor类

class HandlerDescriptor {
  HandlerDescriptor(HandlerDescriptor* nextHandler);
  virtual ~HandlerDescriptor();

public:
  int socketNum;
  int conditionSet;
  TaskScheduler::BackgroundHandlerProc* handlerProc;
  void* clientData;

private:
  // Descriptors are linked together in a doubly-linked list:
  friend class HandlerSet;
  friend class HandlerIterator;
  HandlerDescriptor* fNextHandler;
  HandlerDescriptor* fPrevHandler;
};

socketNum:要监听的socket
conditionSet
handlerProc:回调函数
clientData:数据

fNextHandler:又是组织成一个双向链表
fPrevHandler

5.2.2 HandlerDescriptor基本实现

HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)
  : conditionSet(0), handlerProc(NULL) {
  // Link this descriptor into a doubly-linked list:
  if (nextHandler == this) { // initialization
    fNextHandler = fPrevHandler = this;
  } else {
    fNextHandler = nextHandler;
    fPrevHandler = nextHandler->fPrevHandler;
    nextHandler->fPrevHandler = this;
    fPrevHandler->fNextHandler = this;
  }
}

HandlerDescriptor::~HandlerDescriptor() {
  // Unlink this descriptor from a doubly-linked list:
  fNextHandler->fPrevHandler = fPrevHandler;
  fPrevHandler->fNextHandler = fNextHandler;
}

构造函数,是添加链表的过程,经过努力的分析,我知道了,之前没有好好理解这个this指针,所以到这里就卡壳了,this指针是指自己的对象,你们听听这不是废话么,所以下面说几个例子:
p.show(); 如果show函数里面有this指针,这个this指针就是指向p
handler = new HandlerDescriptor(fHandlers.fNextHandler); 构造函数的时候 ,这个this其实指向的就是handler这个新申请的变量,所以懂了么。

构造函数的传参,就是下一个handler,也就是我现在申请了一个新的handler(this),它的下一个handler是nextHandler,这种头插法的方式,仔细看看4个链表的转换也是可以理解的。

析构的时候,是析构了哪个对象,就把这个对象的结点给释放了。

5.2.3 HandlerIterator类

迭代器的类,就是迭代HandlerDescriptor。来看看类的定义:

class HandlerIterator {
public:
  HandlerIterator(HandlerSet& handlerSet);
  virtual ~HandlerIterator();

  HandlerDescriptor* next(); // returns NULL if none
  void reset();

private:
  HandlerSet& fOurSet;			  // 这个是管理这个链表的类
  HandlerDescriptor* fNextPtr;    //这个指针是可以移动的,指向HandlerDescriptor的一个结点
};

比较简单,但是还是要看看实现。

5.2.4 HandlerIterator基本实现

//构造函数赋值,fOurSet是HandlerSet类型,管理HandlerDescriptor类。
HandlerIterator::HandlerIterator(HandlerSet& handlerSet)
  : fOurSet(handlerSet) {
  reset();
}

HandlerIterator::~HandlerIterator() {
}

// 把fNextPtr指针复位到开始的位置
void HandlerIterator::reset() {
  fNextPtr = fOurSet.fHandlers.fNextHandler;
}

//获取下一个结点
HandlerDescriptor* HandlerIterator::next() {
  HandlerDescriptor* result = fNextPtr;
  if (result == &fOurSet.fHandlers) { // no more   //如果头指针等于自己,就相当于没有结点,所以返回NULL
    result = NULL;
  } else {						
  // 这个说明有,因为构造的时候,已经给fNextPtr赋值了,所以这个可以取到下一个结点的指针
    fNextPtr = fNextPtr->fNextHandler;
  }

  return result;
}

不是很难,理解了链表就可以了,这个迭代器还是比较简单的。

5.2.5 HandlerSet 类

class HandlerSet {
public:
  HandlerSet();
  virtual ~HandlerSet();

  void assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);
  void clearHandler(int socketNum);
  void moveHandler(int oldSocketNum, int newSocketNum);

private:
  HandlerDescriptor* lookupHandler(int socketNum);

private:
  friend class HandlerIterator;
  HandlerDescriptor fHandlers;    //链表的头结点
};

HandlerSet是管理HandlerDescriptor的类,接下来分析一下这个。

5.2.6 HandlerSet构造函数

HandlerSet::HandlerSet()
  : fHandlers(&fHandlers) {
  fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...
}

构造函数还是一如既往的初始化,socketnum初始化为-1。
这个调用父类的构造函数有点难理解,确实有点难受,不过说通了就好。
fHandlers(&fHandlers)
this 是 &fHandlers
nextHandler是&fHandlers
所以两个值相等,就是初始化,把next和prev的指针指向自己。

5.2.7 分配handler

void HandlerSet
::assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData) {
  // First, see if there's already a handler for this socket:
  HandlerDescriptor* handler = lookupHandler(socketNum);
  if (handler == NULL) { // No existing handler, so create a new descr:
    handler = new HandlerDescriptor(fHandlers.fNextHandler);				// 
    handler->socketNum = socketNum;
  }

  handler->conditionSet = conditionSet;
  handler->handlerProc = handlerProc;
  handler->clientData = clientData;
}

lookupHandler是通过socketNum查找到对应的handler号,然后在判断handler是否存在,如果不存在,就重新申请一个结点,并把socket赋值,如果存在,就直接修改原来的值。

重点看一下添加链表的过程,handler = new HandlerDescriptor(fHandlers.fNextHandler)
这时候的this是handler
nextHandler是fHandlers.fNextHandler 其实就是&fHandlers
这两个地址明显不相等,所以进入添加过程。这就是一个双向链表的头插法,所以就把这handler插入到链表中。

5.2.8 查询handler

HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) {
  HandlerDescriptor* handler;
  HandlerIterator iter(*this);
  while ((handler = iter.next()) != NULL) {
    if (handler->socketNum == socketNum) break;
  }
  return handler;
}

按照socketNum去查找对应的handler,函数内部使用了迭代器。就是我们上面讲的。

5.2.9 删除handler

void HandlerSet::clearHandler(int socketNum) {
  HandlerDescriptor* handler = lookupHandler(socketNum);
  delete handler;
}

先通过socketNum找到对应的handler之后,再释放内存。

5.2.10 moveHandler

void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) {
  HandlerDescriptor* handler = lookupHandler(oldSocketNum);
  if (handler != NULL) {
    handler->socketNum = newSocketNum;
  }
}

修改socketNum。

5.2.11 析构函数

HandlerSet::~HandlerSet() {
  // Delete each handler descriptor:
  while (fHandlers.fNextHandler != &fHandlers) {
    delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler
  }
}

循环释放对应的handler的内存。

5.3 Socket I/O 事件处理任务调度

最后一个类,完成这个类之后,基础部分就完成了。但是这个类也是难度较大的,没看到铺垫了这么久了。

5.3.1 BasicTaskScheduler类

class BasicTaskScheduler: public BasicTaskScheduler0 {
public:
  static BasicTaskScheduler* createNew(unsigned maxSchedulerGranularity = 10000/*microseconds*/);
    // “ maxSchedulerGranularity”(默认值:10毫秒)指定返回事件循环以处理非套接字或基于非计时器的事件(例如“触发的事件”)之前等待的最长时间(在“ select()”中) 。
    // 您可以更改此设置(但前提是您仅知道自己在做什么!)或将其设置为0,以不指定此类最大时间。
    // (仅当您知道将不使用“事件触发器”时,才应将其设置为0。)
  virtual ~BasicTaskScheduler();

protected:
  BasicTaskScheduler(unsigned maxSchedulerGranularity);
      // called only by "createNew()"

  static void schedulerTickTask(void* clientData);
  void schedulerTickTask();

protected:
  // Redefined virtual functions:
  virtual void SingleStep(unsigned maxDelayTime);

  virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData);
  virtual void moveSocketHandling(int oldSocketNum, int newSocketNum);

protected:
  unsigned fMaxSchedulerGranularity;    //最大延时

  // To implement background operations:
  int fMaxNumSockets;     // 最大socket号
  fd_set fReadSet;		// 
  fd_set fWriteSet;
  fd_set fExceptionSet;

private:
#if defined(__WIN32__) || defined(_WIN32)
  // Hack to work around a bug in Windows' "select()" implementation:
  int fDummySocketNum;
#endif
};

5.3.2 createNew()

老办法,还是直接创建一个新的对象,live555的做法,都是把构造函数保护化,然后通过creatNew直接创建一个新对象。

BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranularity) {
	return new BasicTaskScheduler(maxSchedulerGranularity);
}

5.3.3 构造函数

BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
  : fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0)
#if defined(__WIN32__) || defined(_WIN32)
  , fDummySocketNum(-1)
#endif
{
  FD_ZERO(&fReadSet);
  FD_ZERO(&fWriteSet);
  FD_ZERO(&fExceptionSet);

  if (maxSchedulerGranularity > 0) schedulerTickTask(); // 确保我们经常处理事件
}

构造函数还是一直的初始化,但是会默认的调用父类的构造函数,这个就比较多变量,到时候用到再看,就是调度的事件怎么就看不懂呢。

以后再看。

5.3.4 添加调度事件

//目前不理解什么意思
void BasicTaskScheduler::schedulerTickTask(void* clientData) {
  ((BasicTaskScheduler*)clientData)->schedulerTickTask();
}

void BasicTaskScheduler::schedulerTickTask() {
  scheduleDelayedTask(fMaxSchedulerGranularity, schedulerTickTask, this);       //添加第一个延时任务
}

看不懂,以后再看。

5.3.5 操作函数

void BasicTaskScheduler
  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
  if (socketNum < 0) return;
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
  if (socketNum >= (int)(FD_SETSIZE)) return;
#endif
  FD_CLR((unsigned)socketNum, &fReadSet);   //  将一个给定的文件描述符从集合中删除
  FD_CLR((unsigned)socketNum, &fWriteSet);
  FD_CLR((unsigned)socketNum, &fExceptionSet);
  if (conditionSet == 0) {     				// 为0的时候,删除
    fHandlers->clearHandler(socketNum);
    if (socketNum+1 == fMaxNumSockets) {
      --fMaxNumSockets;
    }
  } else {					//不为0创建
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);    //  添加sockect handler到双链表中
    if (socketNum+1 > fMaxNumSockets) {
      fMaxNumSockets = socketNum+1;
    }
	//把socket添加到集合中
    if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
    if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
    if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
  }
}

这个函数把添加和删除都集成了。
conditionSet=0的时候就是删除,conditionSet不等于0时就是添加,添加的时候就按照conditionSet这个变量的能力级,添加到对应的FD_SET集合中。

5.3.6 修改socketnum

void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
  if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
  if (oldSocketNum >= (int)(FD_SETSIZE) || newSocketNum >= (int)(FD_SETSIZE)) return; // sanity check
#endif
  if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
  if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
  if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
  fHandlers->moveHandler(oldSocketNum, newSocketNum);

  if (oldSocketNum+1 == fMaxNumSockets) {
    --fMaxNumSockets;
  }
  if (newSocketNum+1 > fMaxNumSockets) {
    fMaxNumSockets = newSocketNum+1;
  }
}

修改socket接口比较简单,把就的socket从FD_SET集合中删除,再把新的socket添加到FD_SET集合中。

5.3.7 事件循环的单次迭代

要想看这个函数,也了解一个select函数,这里有一个链接讲的不错,可以先看看:https://www.cnblogs.com/alantu2018/p/8612722.html

#ifndef MILLION
#define MILLION 1000000
#endif

void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
	//fd_set   表示一个集合
  fd_set readSet = fReadSet; // make a copy for this select() call
  fd_set writeSet = fWriteSet; // ditto
  fd_set exceptionSet = fExceptionSet; // ditto

  DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();     //下一个事件的时间差
  struct timeval tv_timeToDelay;
  tv_timeToDelay.tv_sec = timeToDelay.seconds();
  tv_timeToDelay.tv_usec = timeToDelay.useconds();
  // 很大的“ tv_sec”值会导致select()失败。
  // 请勿使其超过100万秒(11.5天)
  const long MAX_TV_SEC = MILLION;
  if (tv_timeToDelay.tv_sec > MAX_TV_SEC) {    //判断获取的参数
    tv_timeToDelay.tv_sec = MAX_TV_SEC;
  }
  // 还要检查我们的“ maxDelayTime”参数(如果它> 0):
  if (maxDelayTime > 0 &&						// 如果我们的下一个时间的时间差比最大延时大,我们就要把这个阻塞的时间改为最大延时
      (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||
       (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&
	tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
    tv_timeToDelay.tv_sec = maxDelayTime/MILLION;
    tv_timeToDelay.tv_usec = maxDelayTime%MILLION;
  }

  // selet
  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
	//  执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1
  if (selectResult < 0) {    //返回失败的话,描述符是不会被改变的
#if defined(__WIN32__) || defined(_WIN32)
    int err = WSAGetLastError();
    // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
    // it was called with no entries set in "readSet".  If this happens, ignore it:
    if (err == WSAEINVAL && readSet.fd_count == 0) {
      err = EINTR;
      // To stop this from happening again, create a dummy socket:
      if (fDummySocketNum >= 0) closeSocket(fDummySocketNum);
      fDummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
      FD_SET((unsigned)fDummySocketNum, &fReadSet);
    }
    if (err != EINTR) {
#else
    if (errno != EINTR && errno != EAGAIN) {
#endif
	// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
	perror("BasicTaskScheduler::SingleStep(): select() fails");
	// Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number
	// that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()",
	// to assist in debugging:
	fprintf(stderr, "socket numbers used in the select() call:");
	for (int i = 0; i < 10000; ++i) {				// 用于测试指定的文件描述符是否在该集合中
	  if (FD_ISSET(i, &fReadSet) || FD_ISSET(i, &fWriteSet) || FD_ISSET(i, &fExceptionSet)) {
	    fprintf(stderr, " %d(", i);
	    if (FD_ISSET(i, &fReadSet)) fprintf(stderr, "r");
	    if (FD_ISSET(i, &fWriteSet)) fprintf(stderr, "w");
	    if (FD_ISSET(i, &fExceptionSet)) fprintf(stderr, "e");
	    fprintf(stderr, ")");
	  }
	}
	fprintf(stderr, "\n");
#endif
	internalError();
      }
  }

  // 调用一个可读套接字的处理程序函数:
  HandlerIterator iter(*fHandlers);
  HandlerDescriptor* handler;
  // 为了确保通过处理程序的前进,请从我们处理的最后一个套接字号开始:
  if (fLastHandledSocketNum >= 0) {
    while ((handler = iter.next()) != NULL) {
      if (handler->socketNum == fLastHandledSocketNum) break;
    }
    if (handler == NULL) {
      fLastHandledSocketNum = -1;
      iter.reset(); // start from the beginning instead
    }
  }
  while ((handler = iter.next()) != NULL) {
    int sock = handler->socketNum; // alias
    int resultConditionSet = 0;
    if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
    if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
    if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
    if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
      fLastHandledSocketNum = sock;			//这里保存了最后一个socket的num
          // 注意:我们在调用处理程序之前设置了“ fLastHandledSocketNum”,以防处理程序再次调用“ doEventLoop()”。
      (*handler->handlerProc)(handler->clientData, resultConditionSet);
      break;
    }
  }
  if (handler == NULL && fLastHandledSocketNum >= 0) {
    // 我们没有调用处理程序,但是我们没有检查所有的处理程序,因此请从头开始重试:
    iter.reset();
    while ((handler = iter.next()) != NULL) {
      int sock = handler->socketNum; // alias
      int resultConditionSet = 0;
      if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
      if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
      if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
      if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
	fLastHandledSocketNum = sock;
	    // Note: we set "fLastHandledSocketNum" before calling the handler,
            // in case the handler calls "doEventLoop()" reentrantly.
	(*handler->handlerProc)(handler->clientData, resultConditionSet);
	break;
      }
    }
    if (handler == NULL) fLastHandledSocketNum = -1;//因为我们没有调用到处理程序
  }

  // 还可以处理任何新触发的事件(请注意,在触发套接字处理程序修改可读套接字集的情况下,我们在调用套接字处理程序之后执行此操作)。
  if (fTriggersAwaitingHandling != 0) {
    if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
      // Common-case optimization for a single event trigger:
      fTriggersAwaitingHandling &=~ fLastUsedTriggerMask;
      if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
	(*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
      }
    } else {
      // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
      unsigned i = fLastUsedTriggerNum;
      EventTriggerId mask = fLastUsedTriggerMask;

      do {
	i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
	mask >>= 1;
	if (mask == 0) mask = 0x80000000;

	if ((fTriggersAwaitingHandling&mask) != 0) {
	  fTriggersAwaitingHandling &=~ mask;
	  if (fTriggeredEventHandlers[i] != NULL) {
	    (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
	  }

	  fLastUsedTriggerMask = mask;
	  fLastUsedTriggerNum = i;
	  break;
	}
      } while (i != fLastUsedTriggerNum);
    }
  }

  // 还应处理任何可能由于延迟而导致的事件。
  fDelayQueue.handleAlarm();
}

这个函数虽然长,但是也就是做了几个事:

  1. 根据定时器任务列表中,距当前时间最近的任务所需执行的时间点以及传入的最大延迟时间,计算 select() 所能够等待地最长时间。

  2. 执行 select() 等待 socket 上的时间。

  3. select() 超时或某个 socket 上的 I/O 事件到来,首先执行发生 I/O 事件的 socket 的 I/O 事件处理程序。这个函数一次最多执行一个 socket 上的 I/O 处理程序。

  4. 执行用户事件处理程序。也是一次最多执行一个。

  5. 执行定时器任务,同样是一次最多执行一个。

自此这个live555基本知识已经讲完了,是不是感觉每一个模块都知道干啥了,就是不知道整体的,这个等以后分析到有使用到这个模块的时候,再画一个思维导图,来一个整体的认识。

你可能感兴趣的:(LIVE555)