Live555源码阅读笔记(三):BasicUsageEnvironment 目录详解-BasicTaskScheduler、DelayQueue、HandlerSet

Live555源码阅读笔记(一):源码介绍文档 及 源码目录结构

Live555源码阅读笔记(二):UsageEnvironment 目录详解

Live555源码阅读笔记(三):BasicUsageEnvironment 目录详解

目录

一、BasicUsageEnvironment 目录介绍

二、阅读代码

1、BasicUsageEnvironment0 类

2、BasicUsageEnvironment 类

3、BasicTaskScheduler0 类

3.1、BasicTaskScheduler0 类的属性:

3.2、BasicTaskScheduler0 类的方法:

4、BasicTaskScheduler 类

4.1、BasicTaskScheduler 类的属性:

4.2、BasicTaskScheduler 类的方法:

5、Timeval类、DelayInterval类、_EventTime类

6、DelayQueueEntry类、DelayQueue类、AlarmHandler类

7、HandlerDescriptor类、HandlerSet类、HandlerIterator类

7.1 HandlerDescriptor 类

7.2 HandlerSet 类

7.3 HandlerIterator 类

8、BasicHashTable 类

总结:


一、BasicUsageEnvironment 目录介绍

 

BasicUsageEnvironment 目录总共有12个源码文件,定义了“UsageEnvironment”类的一个具体实现(即子类),用于输出错误/警告信息;也定义了"TaskScheduler"的具体实现(子类),用于任务调度;还有哈希表的具体实现。

注意:“UsageEnvironment”类和"TaskScheduler"类是定义在 UsageEnvironment 目录的,需要了解更仔细的可以看上一篇文章(UsageEnvironment 目录详解)。

二、阅读代码

这个目录代码也简单,实现了 UsageEnvironment 类的两个子类 BasicUsageEnvironment0 类和BasicUsageEnvironment 类;实现了 TaskScheduler 类的两个子类 BasicTaskScheduler0 类和 BasicTaskScheduler 类;实现了 HashTable 类的子类 BasicHashTable;还有其他的关于延时队列的和时间、描述符队列的类,下面逐个看一下,目的是看完之后对代码结构以及这几个类有个基本的认识,再结合代码,更好地理解。

BasicUsageEnvironment 、BasicTaskScheduler、BasicHashTable的继承图:

      

1、BasicUsageEnvironment0 类

由继承顺序,我们先看 BasicUsageEnvironment0 类的源码,BasicUsageEnvironment0 类的父类是 UsageEnvironment 类,由于 BasicUsageEnvironment0 类没有实现父类的所有纯虚函数,所以BasicUsageEnvironment0 类也是一个抽象类,不能直接实例化。

BasicUsageEnvironment0 类主要是重写了 UsageEnvironment 类中与“错误/警告信息字符串” 相关的函数,函数实现也比较简单,没有需要特殊说明的。

virtual MsgString getResultMsg() const;
virtual void setResultMsg(MsgString msg);
virtual void setResultMsg(MsgString msg1,MsgString msg2);
virtual void setResultMsg(MsgString msg1,MsgString msg2,MsgString msg3);
virtual void setResultErrMsg(MsgString msg, int err = 0);
virtual void appendToResultMsg(MsgString msg);
virtual void reportBackgroundError();

2、BasicUsageEnvironment 类

BasicUsageEnvironment 类的父类是 BasicUsageEnvironment0 类,它实现了 BasicUsageEnvironment0 类没实现的其他纯虚函数,并且没有声明新的纯虚函数,所以这个类不是抽象类,由于其构造函数 BasicUsageEnvironment 是 protected 属性的,所以这个类不能直接实例化,只能通过类内部定义的 createNew 函数来获取到指向 BasicUsageEnvironment 的指针,再通过指针使用该类。

// new 一个BasicUsageEnvironment对象,并将指针作为返回值
static BasicUsageEnvironment* createNew(TaskScheduler& taskScheduler);

下面是重写 UsageEnvironment 类跟 “重载<<运算符” 相关的函数,它的实现是直接将输入参数通过fprintf 函数打印到 stderr(标准错误文件描述符),直接看代码即可。

UsageEnvironment& BasicUsageEnvironment::operator<<(char const* str) {
  if (str == NULL) str = "(NULL)"; // sanity check
  fprintf(stderr, "%s", str); return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(int i) {
  fprintf(stderr, "%d", i); return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(unsigned u) {
  fprintf(stderr, "%u", u); return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(double d) {
  fprintf(stderr, "%f", d); return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(void* p) {
  fprintf(stderr, "%p", p); return *this;
}

3、BasicTaskScheduler0 类

BasicTaskScheduler0 类的父类是 TaskScheduler 类,由于 BasicTaskScheduler0 类仍存在纯虚函数,所以它也是一个抽象类,不能直接实例化。

3.1、BasicTaskScheduler0 类的属性

fDelayQueue  变量,延时队列,用于实现延时操作;

fHandlers 变量,处理函数(处理程序)的集合,用于实现后台读取;
fLastHandledSocketNum 变量,上次处理的描述符;

fTriggersAwaitingHandling 变量,按位操作,记录了“等待处理”的触发器id;
fLastUsedTriggerMask 变量,按位操作,记录了上次使用的触发器id;
fTriggeredEventHandlers 变量,与触发器id对应的 函数指针数组;
fTriggeredEventClientDatas 变量,传给函数的组成的数组;
fLastUsedTriggerNum 变量,记录已使用的触发器数量。

3.2、BasicTaskScheduler0 类的方法

类的开头声明了一个纯虚函数:

 virtual void SingleStep(unsigned maxDelayTime = 0) = 0;

接着,重写了父类 TaskScheduler 关于 任务调度事件触发 相关的函数,实现过程使用到DelayInterval、AlarmHandler、DelayQueueEntry等类,这些类在后面几个小节有介绍:

//做一些初始化变量的工作,new 了一个 HandlerSet 
BasicTaskScheduler0();

// new 一个定时处理对象(AlarmHandler),并添加到延时队列
virtual TaskToken scheduleDelayedTask(int64_t microseconds,TaskFunc* proc,void* clientData);

// 根据令牌从延时队列里删除一个任务
virtual void unscheduleDelayedTask(TaskToken& prevTask);

// 一直循环调用 SingleStep 处理
virtual void doEventLoop(char volatile* watchVariable);

// 把 eventHandlerProc 注册成一个事件触发器,并返回触发器id,最多可以创建 32 个
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc);

// 根据事件触发器id,删除事件触发器
virtual void deleteEventTrigger(EventTriggerId eventTriggerId);

// 触发指定 id 的事件
virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL);

4、BasicTaskScheduler 类

BasicTaskScheduler 类的父类是 BasicTaskScheduler0 类,它重写了 BasicTaskScheduler0 类和 TaskScheduler 类的所有纯虚函数,但其构造函数 BasicTaskScheduler 是 protected 属性的,所以这个类不能直接实例化,只能通过类内部定义的 createNew 函数来获取到指向 BasicTaskScheduler 的指针,再通过指针使用该类。

4.1、BasicTaskScheduler 类的属性

fMaxSchedulerGranularity 变量,最大调度器粒度,表示在事件循环返回前,在select中等待的最长时间,默认是 10ms;

fMaxNumSockets变量,最大的套接字描述符,用于 select 函数
fReadSet 变量,描述符集-读取;
fWriteSet 变量,描述符集-写入;
fExceptionSet 变量,描述符集-异常。

4.2、BasicTaskScheduler 类的方法

public:
// new 一个 BasicTaskScheduler并返回该对象指针
static BasicTaskScheduler* createNew(unsigned maxSchedulerGranularity = 10000/*microseconds*/);

protected:
//构造函数,初始化并调用一次 schedulerTickTask。只在 createNew 函数会被调用
BasicTaskScheduler(unsigned maxSchedulerGranularity);

// 将 clientData 转为 BasicTaskScheduler,再调用 schedulerTickTask()
static void schedulerTickTask(void* clientData);

// 调用 scheduleDelayedTask 创建一个延时任务;
// scheduleDelayedTask的第二个参数是 TaskFunc 类型的,
// 所以第二个参数传的应该是“static void schedulerTickTask(void* clientData)”
void schedulerTickTask();

// 1.调用 select 等待一个时间 tv_timeToDelay
// 2.为一个可读套接字调用处理函数(从上次处理的套接字开始,在描述符队列往后找,找不到再从头找)
// 3.处理任何新触发的事件
virtual void SingleStep(unsigned maxDelayTime);

// 将一个处理函数添加到后台处理(添加到处理函数队列)
virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData);

// 更改处理函数的描述符
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum);

5、Timeval类、DelayInterval类、_EventTime类

这几个类都是和时间相关的且比较简单,放在一起说明。

Timeval 类可以是绝对时间,也可以是时间间隔,是对系统定义的结构体 struct timeval(定义在<sys/time.h>) 的一个扩展。Timeval 类主要实现了8个运算符重载函数(>=, <=, ==, !=, >, <, +=, -=),方便使用。其构造函数是 protected 的,故不能直接实例化对象。

DelayInterval 类继承自 Timeval 类,延时间隔,对 Timeval 类进一步封装。

_EventTime 类继承自 Timeval 类,事件的绝对时间。

6、DelayQueueEntry类、DelayQueue类、AlarmHandler类

DelayQueueEntry 是一个抽象基类,延时队列入口,其作用是实现延时队列(链表)的一个节点,包含了指向自身的两个指针和数据(fDeltaTimeRemaining)。声明了一个友元类,可以用来访问自身的私有数据。

friend class DelayQueue;

属性
        静态变量 tokenCounter,用来计算令牌总个数
        fToken 变量,实例化时返回 tokenCounter 的值;
        fDeltaTimeRemaining 变量,保存剩余时间;
        fNext fPrev 变量,作为双向队列的节点指针;
方法
        handleTimeout 函数,用来处理超时的情况,超时就 delete 当前对象指针;
        DelayQueueEntry 函数,初始化剩余时间,令牌总个数加一。

DelayQueue 继承自 DelayQueueEntry 类,延时队列,是对DelayQueueEntry 的扩展,实现了节点的添加、更新、删除。因为 DelayQueueEntry  声明了DelayQueue 为其友元类,所以DelayQueue 类可以访问到 DelayQueueEntry  的私有数据。

属性
        fLastSyncTime 变量,记录上次同步时间;
方法
        DelayQueue 函数,初始化队列,更新同步时间为现在;
        ~DelayQueue 函数,清空队列,从队列里删除每一个DelayQueueEntry;

        addEntry 函数,同步并添加一个 entry 到队列里​​​​​;​
        updateEntry 函数,更新队列里的 entry 的剩余时间。
        removeEntry 函数,从队列里删除一个 entry ,但没有删除该对象的指针。

        timeToNextAlarm 函数,获取下一个 entry 的剩余时间。
        handleAlarm 函数,处理剩余时间,把剩余时间为0的从队列里彻底删除。
        findEntryByToken 函数,从队列里查找指定的 entry。
        synchronize 函数,计算上次同步到现在的时间,更新"到时间"的节点

AlarmHandler 是 DelayQueueEntry 的子类,用于实现定时处理。

属性
        fProc 变量,函数指针,指向 “到达时间时要调用” 的函数。
        fClientData 变量,传给定时函数的数据。
方法
        handleTimeout 函数,调用定时处理函数,然后 delete 对象指针。

7、HandlerDescriptor类、HandlerSet类、HandlerIterator类

这三个类实现了一个双向链表的必要内容:

  • HandlerDescriptor:定义了链表结点
  • HandlerSet:定义了双向链表,实现增删改查
  • HandlerIterator:链表迭代器,实例化时需要传入链表引用;

7.1 HandlerDescriptor

HandlerDescriptor 类是处理描述符的,记录了与描述符相关的数据(套接字号、函数指针、传给函数的数据),也定义了两个指向自身的执行,在双向链表中可以作为一个节点(包含了数据、指向自身的指针)。同时它声明了两个友元类,可以用来访问自身的私有数据:

friend class HandlerSet;
friend class HandlerIterator;

属性
        socketNum 变量,套接字描述符;
        conditionSet 变量,条件的集合;
        handlerProc 变量,处理函数的 函数指针;
        clientData 变量,传给处理函数的数据;
        fNextHandler 变量,指向下一个节点的指针;
        fPrevHandler 变量,指向上一个节点的指针。
方法
        HandlerDescriptor 函数,将此描述符添加到一个双向链表:
        ~HandlerDescriptor 函数,从双向链表中删除此描述符;

7.2 HandlerSet 类

HandlerSet 类是处理程序(处理函数)的集合。定义了一个 HandlerDescriptor 类头节点,用双向链表来存储描述符数据,实现了描述符的添加、删除、查找等功能。同时它声明了一个友元类,可以用来访问自身的私有数据:

friend class HandlerIterator;

属性
        fHandlers 变量,“处理程序”的描述符;
方法
        HandlerSet 函数,初始化 fHandlers 变量,创建了一个双向链表:
        ~HandlerSet 函数,删除每个“处理程序”描述符;
        assignHandler 函数,从链表找套接字描述符,找不到就new一个并添加到链表;
        clearHandler 函数,从链表删除 socketNum 指定的套接字;
        moveHandler 函数,将旧的套接字号改为新的;
        lookupHandler 函数,从链表中寻找套接字描述符;

7.3 HandlerIterator

HandlerIterator 类是处理程序(处理函数)链表的迭代器,主要实现指针在链表里的偏移。

属性
        fOurSet 变量,“处理程序”的集合;
        fNextPtr 变量,指向链表下个节点的指针;
方法
        HandlerIterator 函数,初始化 fOurSet 变量,创建了一个双向链表:
        reset 函数,使指针重新偏移到链表的第一个节点;
        next 函数,使链表向下偏移一个节点,并返回该节点。

8、BasicHashTable 类

BasicHashTable 类继承自 HashTable,用于实现哈希表,在当前这个目录暂时没使用到,先不看了,等后续看到使用地方再回来补充。

总结:

UsageEnvironment 目录的源码虽然不多,但定义了好十几个类,其中比较重要的类是 BasicUsageEnvironment 类和 BasicTaskScheduler 类,其他类是在实现这两个类的过程中引申出来的,像 延时队列相关的类处理函数队列相关的类 。看文章过程中可以结合代码一起看会更好理解,还有不要想着通过一篇文章就完全理解整个目录的代码了,有些类在这个目录里还没使用到,可以先知道这个类是干嘛的,等待看后面代码用到时再回来细看。这个这个目录的代码,可以知道 Live555 只有一个线程在运行,不断调用 SingleStep 函数来处理;也知道了这个目录分了几小块来实现的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值