revolver基础库的结构图如下:

C++高性能服务框架revolver:base结构分析_开源项目


主要是三部分:reactor模块、关联组件模块、独立组件。

1.reactor


reactor模块主要是实现网络的分时复用设计的模块,可以在同一个线程模式下处理来自网络的读写事件、内部消息事件、定时器事件。以下是reactor的接口定义:

class CReactor
{
public:
....
void set_message_processor(IMessageProcessor* proc){msg_proc_ = proc;};

virtual int32_t open_reactor(uint32_t number_of_handlers) = 0;
virtual int32_t close_reactor() = 0;

virtual int32_t event_loop() = 0;
virtual int32_t stop_event_loop() = 0;

//事件操作
//添加一个事件的监听
virtual int32_t register_handler(CEventHandler *handler, uint32_t masks) = 0;
//删除一个事件的特定监听
virtual int32_t remove_handler(CEventHandler *handler, uint32_t masks) = 0;
//删除一个事件
virtual int32_t delete_handler(CEventHandler *handler, bool del_event_obj = false) = 0;
//定时器操作
//添加一个定时器
virtual uint32_t set_timer(CEventHandler *event_handler, const void *act, uint32_t delay) = 0;
//删除一个定时器
virtual uint32_t cancel_timer(uint32_t timer_id, const void **act) = 0;

protected:
IMessageProcessor* msg_proc_;
};

其中 

msg_proc_是内部消息队列处理器。


reactor的核心函数是event_loop,event_loop是事件轮询函数,所有的分时复用在其中实现,流程伪代码为:


void event_loop()
{
....
//添加被监控的时间
add_event_to_select(...);

//设置SELECT堵塞时间
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = select_delay_ * 1000;

//进行SOCKET集合的事件扫描
int32_t count = ::select(max_fd, &read_set, &write_set, &expeption_set, &timeout);
if(count > 0) {
//进行SOCKET的事件处理
}

//扫描定时器
select_delay_ = timer_queue_.expire();

//扫描内部消息队列
if(msg_proc_ != NULL){
msg_proc_->processor();
}
...
}

event_loop最主要的工作就是扫描事件。



1.1 EventHandler




CEventHandler是处理reactor触发的事件的,当有事件到来,loop_event会找到对应的event handler进行触发, CEventHandler的事件分类枚举:


typedef enum EVENT_MASK
{
MASK_READ = (1 << 0),//读事件
MASK_WRITE = (1 << 1),//写事件
MASK_TIMEOUT = (1 << 2),//超时事件
MASK_EXCEPT = (1 << 3),//异常事件
}EVENT_MASK;

事件判断值采用2进制进位来表示,1个event_value是否是否是读事件


if(event_value & MASK_READ)
{
//进行读事件处理
}


CEventHandler类接口一般只需要继承实现以下几个接口:


public:
virtual int32_t handle_timeout(const void *act, uint32_t timer_id);//超时事件
virtual int32_t handle_input(BASE_HANDLER handle);//socket读事件
virtual int32_t handle_output(BASE_HANDLER handle);//socket写事件
virtual int32_t handle_close(BASE_HANDLER handle, ReactorMask close_mask);//socket关闭
virtual int32_t handle_exception(BASE_HANDLER handle);//socket异常

以上几个接口根据需要进行实现,例如:只需要实现一个定时器,只需要实现 handle_timeout接口。

1.2 queue


revolver的消息队列实现其实很简单,通过一个继承IMessageProcessor并设置到reactor当中进行消息监听。IMessageProcessor是个即插即用的接口。revolver的消息队列定义如下:


template<class T, int32_t CAPACITY>
class BaseQueue_T
{
public:
BaseQueue_T()
{
data_ = new T[CAPACITY];
rindex_ = 0;
windex_ = 0;
};

~BaseQueue_T()
{
delete []data_;
};

bool put(const T& element)
{
//像队列中PUT一个消息
return true;
};

bool get(T& element)
{
//从队列中get一下消息
return true;
};

private:
T* data_;
volatile int32_t rindex_;
volatile int32_t windex_;
}

其中rindex_是队列读取位置,windex_是队列写入位置。T表示的是所用的消息类型。



1.3 Socket



C++高性能服务框架revolver:base结构分析_reactor_02



以上SOCKET模块的继承和调用关系,BaseSocket是SOCKET的基础封装,主要是实现些send recv等函数,SocketDgram是UDP Socket的实现,SocketStream是TCP Socket的实现。SocketConnector是TCP连接器,CSocketAcceptor是TCP接收器。具体的可以看对应的代码。值得一提的是windows下的UDP Socket的ICMP处理问题,在UDP Socket在在open函数中有下面一段代码:


#ifdef WIN32 //解决WINSOCK2 的UDP端口ICMP的问题
int32_t byte_retruned = 0;
bool new_be = false;

int32_t status = WSAIoctl(handler_, SIO_UDP_CONNRESET,
&new_be, sizeof(new_be), NULL, 0, (LPDWORD)&byte_retruned, NULL, NULL);
#endif

这段代码是为了解决WINDOWS下reactor接收不到ICMP信号的问题。



1.4定时器


revolver的定时器是个相对较复杂的模块,以后打算用专门的篇幅来介绍。



WINDOWS下的revolver只能用来做调试和测试作用,不可以用来做单独的服务。可以用来做客户端的网络模型。