WebServer解析之定时器Timer类(1)

前提:这个WebServer版本不是牛客网上的那一个,这个版本是https://github.com/linyacool/WebServer.git,用的是Reactor模型

同时,这是自己学习的时候的个人理解,以及个人分析,所以可能分析有误,欢迎指正交流。

Timer类的主要作用

1.TimerNode里面用到了类HttpData,而HttpData类又用到了Channel类。首先HttpData类以及Channel类的作用(此处不细节展开这两个类的具体内容)

HttpData类:主要用于对连接、读、以及写的实际操作处理

Channel类:主要用于回调,与Channel回调与HttpData类中实际处理函数绑定,当Channel中的读、写、连接事件发生的时候,调用HttpData类中实际处理函数去进行读、写、连接。

2.HttpData类与Timer类的关系:

HttpData对象绑定一个Timer对象,当超时了,就在优先队列里面进行删除

同时,Timer对象也绑定了一个HttpData对象,以确定TimerNode结点绑定的具体HttpData对象

Timer类的各函数的具体作用及详细解析

Timer.h文件里主要是定义了三个功能

1.时间结点结构TimerNode

1.1更新时间函数updata()

1.2时间结点是否有效

1.3清除时间结点clearReq()

1.4删除setDelete()

1.5是否删除isDelete()

1.6得到到期时间getExpTime()

2.时间结点的比较struct TimerCmp

3.时间结点的管理类型TimerManager

3.1给HttpData增加时间结点类 addTimer()

3.2处理超期时间 handleExpiredEvent()

一个TimerNode要和一个HttpData类绑定,同时一个HttpData类里面也有一个TiemrNode去进行超时删除操作

#include <unistd.h>
#include <deque>
#include <memory>
#include <queue>
#include "HttpData.h"

class HttpData;//这个是前置声明,说明HttpData里面包含了Timer类,同时Timer类里面也包含了HttpData类,为了解决循环依赖问题。

//时间结点类
class TimerNode
{
public:
    // 每一个HttpData都有一个timernode的对象去进行事件超时的管理
    TimerNode(std::shared_ptr<HttpData> requestDate, int timeout);
    ~TimerNode();
    TimerNode(TimerNode &tn);
    void update(int timeout);//更新到期时间
    bool isValid();//判断是否有效
    void clearReq();//清除结点
    // delete
    void setDeleted() { deleted_ = true; }
    // 判断是否delete,这里设置const是不能够改变成员变量的值
    bool isDeleted() const
    {
        return deleted_;
    }
    // 得到到期时间
    size_t getExpTime() const { return expiredTime_; }

private:
    bool deleted_;       // 判断这个定时器是否删掉的标识符
    size_t expiredTime_; // 到期时间
    std::shared_ptr<HttpData> SPHttpData;
};


/* 一个结构体,这个结构体是去比较a和b的到期时间,使用这两个参数中的 getExpTime() 函数来比较这两个 TimerNode 共享指针的到期时间。如果 a 的到期时间大于 b 的到期时间,则返回 true;否则返回 false。这个是优先级队列里面常用的方法,在class TimerManager里面的std::priority_queue<SPTimerNode, std::deque<SPTimerNode>, TimerCmp> timerNodeQueue;用*/
struct TimerCmp
{
    bool operator()(std::shared_ptr<TimerNode> &a, std::shared_ptr<TimerNode> &b)
    {
        return a->getExpTime() > b->getExpTime();
    }
};



//TimerNode结点的管理类,主要是增加,删除,创建一个优先级队列
class TimerManager
{
public:
    TimerManager();
    ~TimerManager();
//HttpData类里面把Timer加进去
    void addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout);
    void handleExpiredEvent();

private:
//声明,SPTimerNode就i等同于std::shared_ptr<TimerNode>
    typedef std::shared_ptr<TimerNode> SPTimerNode;
    /*这行代码声明了一个名为timerNodeQueue的优先队列,其元素类型为SPTimerNode。该优先队列使用std::deque作为其底层容器,并使用自定义的比较函数TimerCmp来进行元素的比较。*/
    std::priority_queue<SPTimerNode, std::deque<SPTimerNode>, TimerCmp> timerNodeQueue;
};

Timer.cpp

TimerNode::TimerNode(std::shared_ptr<HttpData> requestDate, int timeout) : deleted_(false), SPHttpData(requestDate)
{
    struct timeval now;
    /*gettimeofday(&now,NULL) 是一个用于获取当前时间的函数调用。其中 &now 是一个指向 timeval 结构的指针,NULL 则是一个时区结构的指针。gettimeofday 函数用于获取当前的时间和日期,包括秒数和微秒数。*/
    gettimeofday(&now, NULL);
    /*expiredTime_ 被设置为一个相对时间点,即从当前时间开始经过了 timeout 毫秒之后的时间点。
    首先,它从 now 变量中获取了当前时间的秒数 tv_sec 和微秒数 tv_usec。然后它对当前秒数取模 10000,这样做可能是为了防止超出范围。接着将取模后的秒数乘以 1000,这样得到的是毫秒数。之后,将微秒数除以 1000,这样得到的也是毫秒数。*/
    expiredTime_ = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout;
}

/*这个地方析构TimerNode的时候不会让HttpData删掉(除非已经没有其他共享指针指向TimerNode了),但是会让channel_从epoll中删掉*/
TimerNode::~TimerNode()
{
    // 这个handleClose()函数的实现在HttpData中,主要有三个实现:连接状态改为断开连接,确保在 handleClose 方法执行期间,HttpData 对象不会被意外释放以及移除loop中的channel_
    if (SPHttpData)
        SPHttpData->handleClose();
}

TimerNode::TimerNode(TimerNode &tn) : SPHttpData(tn.SPHttpData), expiredTime_(0)
{
}

/*得到的结果就是新的到期时间 expiredTime_*/
void TimerNode::update(int timeout)
{
    struct timeval now;
    gettimeofday(&now, NULL);
    expiredTime_ = ((now.tv_sec % 10000) * 1000 + (now.tv_usec / 1000)) + timeout;
}

/*查看时间是否有效,逻辑如下:
1.得到现在的时间tmp
2.如果现在<到期时间 return true
3.如果现在>到期时间  return false*/
bool TimerNode::isValid()
{
    struct timeval now;
    gettimeofday(&now, NULL);
    size_t temp = ((now.tv_sec % 10000) * 1000 + (now.tv_usec / 1000));
    if (temp < expiredTime_)
    {
        return true;
    }
    else
    {
        this->setDeleted();
        return false;
    }
}

/*清除关联的 SPHttpData对象,并设置 TimerNode 的状态为已删除。*/
void TimerNode::clearReq()
{
    /*共享指针清楚,如果是没有其他地方调用SPHttpData,那么就析构*/
    SPHttpData.reset();
    this->setDeleted();
}

TimerManager::TimerManager()
{
}

TimerManager::~TimerManager() {}

void TimerManager::addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout)
{
    // 先构建一个timerNode,加入优先级队列,然后与HttpData关联
    SPTimerNode new_node = std::make_shared<TimerNode>(SPHttpData, timeout);
    timerNodeQueue.push(new_node);
    SPHttpData->linkTimer(new_node);//这个函数的实现是在HttpData类中
}

/*1.删除标识为delete的,如果非第一个被标志为delete,不会马上删除,会等到delete这个超时了之后再删除。
原因:第一个好处是不需要遍历优先队列,省时,第二个好处是给超时时间一个容忍的时间,就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除),如果监听的请求在超时后的下一次请求中又一次出现了,
就不用再重新申请RequestData节点了,这样可以继续重复利用前面的RequestData,减少了一次delete和一次new的时间
2.删除超时的
*/
void TimerManager::handleExpiredEvent()
{
    while (!timerNodeQueue.empty())
    {
        SPTimerNode ptimer_now = timerNodeQueue.top();
        if (ptimer_now->isDeleted())
        {
            timerNodeQueue.pop();
        }
        else if (ptimer_now->isValid() == false)
        {
            timerNodeQueue.pop();
        }
        else
        {
            break;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值