一、概述
Channel类中数据成员:
- 描述符fd;
- 需要监听的事件events;
- fd被激活调用的可读/可写/关闭/错误回调函数;
`
const int fd_; //描述符fd;
int events_; //需要监听的事件events;
EventCallback readCallback_; //fd被激活调用的可读回调函数;
EventCallback writeCallback_; //fd被激活调用的可写回调函数;
EventCallback closeCallback_; //fd被激活调用的关闭回调函数;
EventCallback errorCallback_; //fd被激活调用的错误回调函数;
Poller类监听的其实就是一个个Channel对象,Channel可以向Poller注册自己关心的事件,当被激活后调用相应的回调函数。类似libevent的struct event。
二、Channel在整个事件驱动循环中的大致流程
- Acceptor接收到客户端请求,调用TcpServer回调函数;
- TcpServer回调函数中创建TcpConnection对象,代表着一个Tcp连接;
- TcpConnection构造函数中创建Channel对象,保存客户端套接字fd和关心的事件(可读);
- Channel注册自己到所属事件驱动循环(EventLoop) 中的 Poller上;
- Poller开始监听,当发现Channel被激活后将其添加到EventLoop的激活队列中;
- EventLoop在poll返回后处理激活队列中的Channel,调用其处理函数;
- Channel在处理函数中根据被激活的原因调用不同的回调函数(可读/可写等)。
其中,各个类分别是:
- Acceptor监听类,用于监听客户端请求,然后接收客户端;
- TcpServer服务器类,用于管理所有的TcpConnection;
- TcpConnectionTcp连接类,代表一个Tcp连接,内部保存对应的Channel;
- Channel,套接字和相应事件及回调函数的封装,一个事件类;
- Poller,io复用的封装,用于监听Channel
- EventLoop,事件驱动主循环,调用poll函数,管理激活队列,类似libevent的struct event_base
三、函数对象std::function
C++基于对象其实就是利用std::function/std::bind
实现的,作用是绑定某个类的函数指针 。使用方法如:
#include <functional>
typedef std::function<void()> Callback;
void function_bind_test(Callback func)
{
func();/* 这里调用相当于conn->connectionEstablished() */
}
function_bind_test(std::bind(&TcpConnection::connectionEstablished, conn));
std::function/std::bind
为类的成员函数的调用提供更多灵活性,在C++11引入lambda
后逐渐取代了std::bind
,C++14后lambda
支持模板参数后完全取代了std::bind
四、Channel定义如下,成员函数主要就是
- 设置回调函数
set*Callback
- 设置对fd关心的事件,将自己存储的fd及相应的事件注册到Poller中
enable*
- 删除对fd的监听,将其从
Poller
的ChannelMap
中移除disableAll
- 被激活时调用的回调函数
hanleEvent
/*
* Channel其实就是一个事件类,保存fd和需要监听的事件,以及各种回调函数
* 类似libevent的struct event
*/
class Channel : noncopyable
{
public:
/*
* function,可调用函数对象,类似函数指针
* 模板参数是函数标签,例如
* function<int(int, int)> func;
* 这个函数对象的两个参数类型都是int,返回值类型是int
*/
typedef std::function<void()> EventCallback;
typedef std::function<void(Timestamp)> ReadEventCallback;
Channel(EventLoop* loop, int fd);
~Channel();
/* 被激活后调用的函数,内部调用下面设置的几个函数 */
void handleEvent(Timestamp receiveTime);
/* 设置回调函数 */
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); }
/// Tie this channel to the owner object managed by shared_ptr,
/// prevent the owner object being destroyed in handleEvent.
/* 用于保存TcpConnection指针 */
void tie(const std::shared_ptr<void>&);
int fd() const { return fd_; }
int events() const { return events_; }
void set_revents(int revt) { revents_ = revt; } // used by pollers
// int revents() const { return revents_; }
bool isNoneEvent() const { return events_ == kNoneEvent; }
/* 将自己注册到Poller中,或从Poller中移除,或只删除某个事件,update实现 */
void enableReading() { events_ |= kReadEvent; update(); }
void disableReading() { events_ &= ~kReadEvent; update(); }
void enableWriting() { events_ |= kWriteEvent; update(); }
void disableWriting() { events_ &= ~kWriteEvent; update(); }
void disableAll() { events_ = kNoneEvent; update(); }
bool isWriting() const { return events_ & kWriteEvent; }
bool isReading() const { return events_ & kReadEvent; }
// for Poller
int index() { return index_; }
void set_index(int idx) { index_ = idx; }
// for debug
string reventsToString() const;
string eventsToString() const;
void doNotLogHup() { logHup_ = false; }
EventLoop* ownerLoop() { return loop_; }
void remove();
private:
static string eventsToString(int fd, int ev);
void update();
void handleEventWithGuard(Timestamp receiveTime);
static const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_;
const int fd_;
int events_;
int revents_; // it's the received event types of epoll or poll
/*
* 保存fd在epoll/poll中的状态,有:
* 还没有添加到epoll中
* 已经添加到epoll中
* 添加到epoll中,又从epoll中删除了
*/
int index_; // used by Poller.
bool logHup_;
/*
* tie_存储的是TcpConnection类型的指针,即TcpConnectionPtr
* 一个TcpConnection代表一个已经建立好的Tcp连接
*/
std::weak_ptr<void> tie_;
bool tied_;
bool eventHandling_;
bool addedToLoop_;
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
};
五、.cpp
中的几个比较重要的函数介绍
/*
* 由TcpConnection中connectEstablished函数调用
* 作用是当建立起一个Tcp连接后,让用于监测fd的Channel保存这个Tcp连接的弱引用
* tie_是weak_ptr的原因
* weak_ptr是弱引用,不增加引用基数,当需要调用内部指针时
* 可通过lock函数提升成一个shared_ptr,如果内部的指针已经销毁
* 那么提升的shared_ptr是null
* 可以通过是否是null判断Tcp是否还处于连接,因为如果断开,那么这个TcpConnection就被销毁了
*/
void Channel::tie(const std::shared_ptr<void>& obj)
{
tie_ = obj;
tied_ = true;
}
因为Channel
的回调函数其实调用的是TcpConnection
中的函数,所以如果想要调用,就必须知道TcpConnection
的指针,而如果TcpConnection
被销毁了,那么指针调用会出错,所以采用智能指针,又因为Channel
只需要知道TcpConnection
是否还存在,所以不需要使用shared_ptr
,这会增加引用计数,导致TcpConnection
销毁不了(如果close连接,TcpConnection
本应该被销毁,但是因为Channel
中也留有对TcpConnection
的引用,所以不会销毁,糟糕的事情…),所以采用weak_ptr
,不增加引用计数,适当时可以提升为shared_ptr
/*
* 根据fd激活事件的不同,调用不同的fd的回调函数
*/
void Channel::handleEvent(Timestamp receiveTime)
{
/*
* RAII,对象管理资源
* weak_ptr使用lock提升成shared_ptr,此时引用计数加一
* 函数返回,栈空间对象销毁,提升的shared_ptr guard销毁,引用计数减一
*/
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
经典的RAII应用场景,配合智能指针
如果想要判断Channel
所在的TcpConnection
是否仍然存在(没有被关闭),可以直接将weak_ptr
提升为shared_ptr
,如果提升成功(不为NULL),代表TcpConnection
存在,调用回调函数即可。
提升成shared_ptr
后TcpConnection
引用计数变为2,handleEvent
函数返回后局部变量guard
销毁,引用计数恢复到1。
同样的应用还有锁对象,创建时上锁,析构时解锁。
handleEventWithGuard
其实就是根据不同的激活原因滴啊用不同的回调函数,这些回调函数都在TcpConnection
中,也是通过上面std::function/std::bind
设置的,所以在调用前必须判断Channel
所在的TcpConnection
是否还存在
/*
* 根据被激活事件的不同,调用不同的回调函数
*/
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
eventHandling_ = true;
LOG_TRACE << reventsToString();
if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
{
if (logHup_)
{
LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
}
if (closeCallback_) closeCallback_();
}
if (revents_ & POLLNVAL)
{
LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
}
if (revents_ & (POLLERR | POLLNVAL))
{
if (errorCallback_) errorCallback_();
}
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
{
if (readCallback_) readCallback_(receiveTime);
}
if (revents_ & POLLOUT)
{
if (writeCallback_) writeCallback_();
}
eventHandling_ = false;
}