前提:这个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;
}
}
}