muduo库中的Channel类封装了需要监听的fd以及fd感兴趣的事件events和返回的事件revents
TcpServer相当于是muduo库提供给外部编写服务器程序的一个类,相当于一个工具箱,把muduo库有关服务器的编程相关的东西,包括反应堆,事件分发器,事件回调都打包一块了
我们看看它的成员变量
EventLoop就是开启epoll_wait事件循环,可以理解成epoll,我们看看EventLoop具体怎么实现的
EventLoop的成员变量
Poller相当于是epoll抽象的概念,用Poller当做基类,在派生类中实现了poll和epoll,poll和epoll负责事件分发
此外,epoll还需要监听socket。我们注意到,在EventLoop类内除了编译器内置类型,自定义类型只有两个,Poller和Channel
我们看看Channel的一些成员变量
Channel包含了监听的sockfd,感兴趣的事件和发生的事件
Eventloop是事件循环(epoll_wait),相当于事件分发器,包含了Poller(epoll)和Channel(epoll监听的sockfd,以及它对应的事件和最终发生的事件),对应Reactor模型上的多路事件分发器Demultiplex
一个channel属于一个EventLoop,一个EventLoop对应多个channel,一个channel封装了一个fd和这个fd对应的感兴趣的事件以及发生的事件
Channel要向Poller注册感兴趣事件,Poller通知Channel发生的事件,Channel得到关于fd的事件通知会调用预置的回调操作
一个线程对应一个eventloop,一个eventloop有一个poller,一个poller可以监听多个channel,每个channel属于一个eventloop,一个eventloop有很多channel,这就是多路;一个线程监听多路fd
Channel.h
#pragma once
#include "noncopyable.h"
#include "Timestamp.h"
#include <functional>
#include <memory>
class EventLoop;
/**
* Channel理解为通道,封装了sockfd和其感兴趣的事件,如EPOLLIN、EPOLLOUT事件,还包括Poller返回的具体事件
*/
class Channel : noncopyable{
public:
using EventCallBack = std::function<void()>;
using ReadEventCallBack = std::function<void(Timestamp)>;
Channel(EventLoop* loop, int fd); // loop是指针,编译阶段能确定大小,不需要知道EventLoop的定义,无需包含头文件
~Channel();
// 得到poller通知以后,处理事件
void handleEvent(Timestamp receiveTime); // 由于receiveTime是对象,不是指针,编译阶段需要确定对象的大小,所以只有Timestamp的声明不够,需要有类定义,即需要包含头文件
// 设置回调操作 不需要形参的局部对象,将左值转换成右值
void setReadCallBack(ReadEventCallBack cb){ readCallback_ = std::move(cb); }
void setWriteCallBack(EventCallBack cb){ writeCallBack_ = std::move(cb); }
void setCloseCallBack(EventCallBack cb){ closeCallBack_ = std::move(cb); }
void errorCloseCallBack(EventCallBack cb){ errorCallBack_ = std::move(cb); }
// 防止channel被手动remove,channel还在执行回调操作,处理fd上的数据
void tie(const std::shared_ptr<void>&);
int fd() const { return fd_; }
int events() const{ return events_; }
// 设置revents_,poller(poll/epoll)在监听fd,发生事件后调用set_revents告诉channel发生了什么事件
void set_revents(int revt){ revents_ = revt; }
void enableReading(){
events_ |= kReadEvent; // 相当于是添加感兴趣的事件,把events_中和read相关的位 置1
update(); // epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &events_)
}
void disableReading(){
events_ &= ~kReadEvent; // 删除读事件,取反后&,会把events_中和read相关的位 置0
update();
}
void enableWriting(){ events_ |= kWriteEvent; update(); }
void disableWriting(){ events_ &= ~kWriteEvent; update(); }
void disableAll(){ events_ = kNoneEvent; update(); }
bool isNoneEvent() const{ return events_ == kNoneEvent; }
bool isWritingEvent() const{ return events_ & kWriteEvent; }
bool isReadingEvent() const{ return events_ & kReadEvent; }
int index(){ return index_; }
void set_index(int idx){ index_ = idx; }
// one thread per thread
// 一个channel对应一个EventLoop,一个EventLoop对应多个channel,一个channel包含一个fd以及这个fd对应的感兴趣的事件以及发生的事件
EventLoop* ownerLoop(){ return loop_; }
// 用于删除channel
void remove();
private:
void update();
void handleEventWithGaurd(Timestamp receiceTime);
// 表示当前fd的状态,对什么事件感兴趣
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_; // 归属于哪个事件循环 epoll_wait
const int fd_; // Poller监听的对象
int events_; // 注册fd感兴趣的事件
int revents_; // Poller返回的具体发生的事件
int index_;
std::weak_ptr<void> tie_; // 绑定自己,防止channel被手动remove后,我们还在使用channel
bool tied_;
// Poller通知Channel发生的事件会放在revents_,所以Channel负责调用具体事件的回调操作
ReadEventCallBack readCallback_;
EventCallBack writeCallBack_;
EventCallBack closeCallBack_;
EventCallBack errorCallBack_;
};
Channel类析构函数源码如下:
Channel::~Channel()
{
assert(!eventHandling_); // handleEventWithGuard调用会到函数处理事件的时候会使用,保证没有正在被处理的回调操作
assert(!addedToLoop_); // addedToLoop_在update的时候会被置为true,remove的时候被置为false,assert保证channel要么没使用过,要么被remove
// 由于当前channel属于某个eventloop,判断当前channel是否在当前事件循环loop_的线程析构
if (loop_->isInLoopThread())
{
assert(!loop_->hasChannel(this)); // 判断当前channel是否属于当前的事件循环loop_
}
}
由于muduo源码中的Channel析构函数没有任何释放资源的操作,我们也就不写了
Channel::~Channel(){}
得到Poller通知后,Channel需要调用,处理事件
handleEvent源码如下:
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_) // weak_ptr tie_用于监听当前channel
{
guard = tie_.lock(); // 把weak_ptr tie_通过lock提升成shared_ptr
if (guard) // 查看tie_观察的对象是否还存活(资源是否被释放)
{
handleEventWithGuard(receiveTime); // 观察的对象还存活,就调用事件处理函数
}
}
else
{
// 如果没有执行tie()方法,tied_就是false,则直接调用事件处理函数
handleEventWithGuard(receiveTime);
}
}
那tie方法什么时候执行?有什么作用呢?
tie方法会在建立新连接时在TcpConnection::connectEstablished中调用,传TcpConnection对象的shared_ptr给参数obj,Channel有一个weak_ptr对象指向Channel所属的TcpConnection对象
这样可以解决TcpConnection对象已经不存在了,但是Channel还注册在Poller,还能感知到Poller的通知并调用Channel的回调方法的问题
tie_是一个weak_ptr,Channel调用回调方法前都会调用handleEvent,这个方法会检查weak_ptr指向的对象是否失效(lock方法),如果失效了就不会调用相应方法了
void Channel::tie(const std::shared_ptr<void>& obj){
// weak_ptr tie_ 观察 shared_ptr obj
tie_ = obj;
tied_ = true;
}
// 得到poller通知以后,处理事件
void Channel::handleEvent(Timestamp receiveTime){
if(tied_){
std::shared_ptr<void> guard = tie_.lock();
if(guard){
handleEventWithGaurd(receiveTime);
}
}else{
handleEventWithGaurd(receiveTime);
}
}
Channel.cc
#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"
#include <sys/epoll.h>
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;
const int Channel::kWriteEvent = EPOLLOUT;
Channel::Channel(EventLoop* loop, int fd)
: loop_(loop)
, fd_(fd)
, events_(0)
, revents_(0)
, index_(-1)
,tied_(false)
{}
Channel::~Channel(){}
void Channel::tie(const std::shared_ptr<void>& obj){
// weak_ptr tie_ 观察 shared_ptr obj
tie_ = obj;
tied_ = true;
}
/*
向Poller(poll/epoll)更新fd感兴趣的事件,就是epoll_ctl
EventLoop包含Channel、Poller,在Channel中的update方法需要更新epoll内核事件表中fd感兴趣的事件,
然而epoll内核事件表需要由Poller操作,所以Channel告诉EventLoop,需要Poller更新epoll内核事件表
*/
void Channel::update(){
// 通过Channel所属的EventLoop,调用Poller的方法,注册fd的events事件
loop_->updateChannel(this);
}
void Channel::remove(){
// 在当前channel所属的eventloop中删除当前channel
loop_->removeChannel(this);
}
// 得到poller通知以后,处理事件
void Channel::handleEvent(Timestamp receiveTime){
if(tied_){
std::shared_ptr<void> guard = tie_.lock();
if(guard){
handleEventWithGaurd(receiveTime);
}
}else{
handleEventWithGaurd(receiveTime);
}
}
// 根据接收到的事件,执行对象的回调函数处理
void Channel::handleEventWithGaurd(Timestamp receiveTime){
// LOG_INFO("in function : %s\n", __FUNCTION__);
// LOG_INFO("line : %s\n", __LINE__);
LOG_INFO("channel handleEvent revents: %d\n", revents_);
if((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)){
// 读写都关闭 && 没有读事件,表示出问题了
if(closeCallBack_){
// 回调函数对象不空,执行回调
closeCallBack_();
}
}
if(revents_ & EPOLLERR){
if(errorCallBack_){
errorCallBack_();
}
}
if(revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)){
if(readCallBack_){
readCallBack_(receiveTime);
}
}
if(revents_ & EPOLLOUT){
if(writeCallBack_){
writeCallBack_();
}
}
}