Channel 是事件通道,它绑定某个文件描述符 fd
,注册感兴趣的事件(如读/写),并在事件发生时分发给对应的回调函数。
const int Channel::KNoneEvent=0;//用于表示该 Channel 的 fd 没有注册到 epoll 中,或者已经被移除了。 const int Channel::KReadEvent=EPOLLIN|EPOLLPRI;//..需要监听“读”相关事件 const int Channel::KWriteEvent=EPOLLOUT;//需要监听“写”事件。
私有成员变量和部分函数实现 void update();//:当感兴趣的事件(events_)发生改变时,调用 update() 通知 EventLoop 去更新 Poller 中对该 fd 的监听事件。 void handleEventWithGuard(Timestamp receiveTime);//真正执行回调的核心函数。 static const int KNoneEvent;//表示不关心任何事件,对应 0 static const int KReadEvent;//表示关心读事件,如 EPOLLIN | EPOLLPRI static const int KWriteEvent;//表示关心写事件,如 EPOLLOUT EventLoop*loop_;//事件循环 const int fd_;//fd,poller监听的对象 int events_;//注册fd感兴趣的事件 int revents_;//poller返回的具体发生的事件 int index_;//用于 poll 模型时,在 pollfd 数组中的下标 std::weak_ptr<void> tie_;//用于解决对象生命周期问题,防止回调执行时对象已被销毁 bool tied_;//表示当前 Channel 是否已绑定(tie)了一个对象 //因为channel通道里面能够获知fd最终发生的具体的事件revents,所以他负责调用具体事件的回调操作 ReadEventCallback readCallback_; EventCallback writeCallback_; EventCallback closeCallback_; EventCallback errorCallback_; using EventCallback=std::function<void()>; using ReadEventCallback=std::function<void(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 setErrorCallback(EventCallback cb) { errorCallback_=std::move(cb); } int fd()const {return fd_; } int events()const {return events_;}//返回当前 Channel 关注的事件集合 void set_revents(int revt)//设置实际发生的事件集合 revents_ { revents_=revt; } /设置fd相应的事件状态 void enableReading() { events_|=KReadEvent;//在现有的关注事件中,加上 "可读事件"(KReadEvent)。 update();//调用 update() 方法,更新底层的 epoll(或 poll/kqueue)注册事件 } void disableReading() { events_&=~KReadEvent;//清除写事件 update(); } void enableWriting() { events_|=KWriteEvent;//清除写事件 update(); } void disableWriting() { events_&=~KWriteEvent;//清除写事件 //按位或,只要有一位是1就是1 update(); } void disableAll() { events_&=KNoneEvent; update(); } //返回fd当前的事件状态 bool isNoneEvent() const { return events_ ==KNoneEvent;//判断当前 events_ 是否为 KNoneEvent,即没有关注任何事件。 } bool isWriting() const { return events_ &KWriteEvent; //判断当前 events_ 是否包含“写事件” (KWriteEvent)。 } bool isReading() const { return events_ &KReadEvent;//判断当前 events_ 是否包含“读事件” (KReadEvent)。 } //按位与就是逐位比较两个二进制数,只有两个对应位都为1时,结果位才是1,在事件处理中用它可以检测某个事件标志是否被设置,非常高效且方便 int index()//index_ 通常用来标识 Channel 在 Poller(比如 poll 或 epoll)内部的数据结构中的位置,比如在 pollfd 数组里的下标。 { return index_; //在 poll 系统调用里,内核会维护一个 pollfd 数组,数组里每个元素对应一个文件描述符和它关注的事件。 } void setindex(int idx) { index_=idx; } //one loop per thread EventLoop*ownerLoop() { return loop_; }//一个 Channel 总是绑定到某个事件循环 EventLoop,它负责事件的分发与回调调度。
tie_作用:用于解决对象生命周期的问题,防止悬空指针导致崩溃。
- 假设某个连接因为对方关闭了 socket,于是服务器也准备销毁这个
TcpConnection
; - 然而此时
epoll_wait()
已经返回,Channel::handleEvent()
正准备调用绑定的回调(比如handleRead()
); - 此时
TcpConnection
对象已经被销毁,this
成为 悬空指针,调用handleRead()
会崩溃!
函数实现
Channel::Channel(EventLoop *loop,int fd):loop_(loop),fd_(fd),events_(0),revents_(0),index_(-1),tied_(false) { } Channel::~Channel() { } //tie_ 是一个 std::weak_ptr<void> 类型,它保存了一个指向某个对象(通常是 TcpConnection)的弱引用。 //因为 weak_ptr 不增加引用计数,不影响对象生命周期。 //一旦绑定后,Channel 就可以通过 tie_.lock() 检查这个对象是否还活着 void Channel::tie(const std::shared_ptr<void>&obj) { tie_=obj; tied_=true;//在后续 handleEventWithGuard() 中会检查这个标志位,如果未绑定就直接调用回调,不加锁保护 } //是 Channel 和 EventLoop/Poller 之间的桥梁之一,用于将 Channel 对 fd 感兴趣的事件更新到底层的 epoll(或 poll/kqueue)中去。 //让 EventLoop 把这个 Channel 的当前事件状态(events_)传递给 Poller 去更新注册信息; //调用了 enableReading() / enableWriting() / disableAll() 等修改 events_ 状态的方法之后; //Channel 初始化完、注册到 EventLoop 时; //Channel 的事件兴趣(读写)发生变更时; void Channel::update() { loop_->updateChannel(this); } //在channel所属的Eventloop中,把当前的channel删除掉 //Channel::remove() 是将该 Channel 从 Poller 注销、彻底停止事件监听的关键步骤,通常在连接关闭或对象销毁前调用,确保资源释放和事件循环的正确性。 void Channel::remove() { loop_->removeChannel(this); } //内核中 fd 有事件时,通过 epoll_wait() 返回,实际是将发生事件的channel填充到 activeChannels_,EventLoop 收集活跃 Channel,遍历,并依次调用其 handleEvent(),再通过 tie_ 判断资源是否有效,最后执行绑定的回调函数。 void Channel::handleEvent(Timestamp receiveTime)//fd得到poller通知以后,处理事件的函数 { if(tied_)//表示这个 Channel 是否绑定了一个 shared_ptr 管理的资源 { std::shared_ptr<void>guard=tie_.lock();//提升 if(guard) { handleEventWithGuard(receiveTime); } } else//如果是独立 Channel,不绑定也安全。 { handleEventWithGuard(receiveTime); } } //根据poller通知的channel发生的具体事件,由channel负责调用具体的回调操作 void Channel::handleEventWithGuard(Timestamp receiveTime) { LOG_INFO("channel handleEvent revents:%d",revents_); if((revents_&EPOLLHUP) && !(revents_&EPOLLIN))//只有挂起事件,并且没有可读事件(即已经完全关闭连接,读不到数据) { if(closeCallback_) { closeCallback_();//如果用户注册了关闭事件回调函数,就调用它。 } } if(revents_&EPOLLERR)//EPOLLERR:表示文件描述符发生错误,比如连接被重置 { if(errorCallback_) { errorCallback_(); } } if(revents_&(EPOLLIN|EPOLLPRI))//如果注册了读事件回调函数,则调用它,并将事件接收时间传入。 { if(readCallback_) { readCallback_(receiveTime); } } if(revents_&EPOLLOUT)//可写 { if(writeCallback_) { writeCallback_(); } } }