revolver基礎庫的結構圖如下:
主要是三部分:reactor模塊、關聯組件模塊、獨立組件。
1.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_是內部消息隊列處理器。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
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)
{
//進行讀事件處理
}
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
#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信號的問題。