libev的使用——結合Socket編程

  之前自己學過一些libev編程的基礎,這次寫壓測剛好用上了,纔算真正動手寫了些東西,在這裏做一些總結。寫這篇文章是爲了用淺顯易懂的語言幫助大家做一個入門,我自己也是入門程序媛一隻,所以有理解錯誤的地方歡迎指出。

首先推薦幾個我認爲學習libev比較好的blog,最後一個地址是官方文檔,給了我很多幫助:

http://vimersu.win/blog/2014/03/06/libev-study/

http://www.cnblogs.com/dirlt/archive/2011/09/07/2169344.html#sec-1

http://blog.csdn.net/woxiaozhi/article/details/16963641

http://www.cnblogs.com/wunaozai/p/3950249.html

http://wenku.baidu.com/link?url=z021OoHoevOJDNP1IVTmEcWvTKRoNKlfzi96k8gUKvcnvdZxlvW4diCihYlxFYRJVjtHSVtYrN451MIqlyznsmO0wUvjRslNPGOZopSIhP_

一.libev原理

  之前簡單寫過一篇libev的學習:http://blog.csdn.net/cxy450019566/article/details/52416349

  有時間可以看看,沒時間直接看這一篇文章。

  需要理解的第一句話:Libev的核心是一個event loop。一個event loop,通俗講就是一個不停在循環運行的事件。

  需要理解的第二句話:Libev通過分配和註冊watcher對不同類型的事件進行監聽,當監聽被觸發時,進行相應的操作。在一個event loop中,我們可以設定對很多libev支持的事件(見上一篇博客)的監聽watcher,這些事件的監聽是異步進行的,觸發任意一個監聽的事件,都可以根據我們的設定進行某些操作。

  理解了這兩句話,你就可以理解libev的作用,以及怎樣來使用libev。

二.通過簡單示例瞭解libev基本用法

我們從官方示例,來學習libev的基本用法。

// libev需要的頭文件
#include <ev.h>
#include <stdio.h>
 
// 建立我們剛剛說的需要監聽的事件,這些事件類型是libev提供的
// with the name ev_TYPE
//ev_io和ev_timer最爲常用,ev_io爲監聽控制檯輸入,ev_timer爲時間事件
ev_io stdin_watcher;
ev_timer timeout_watcher;
 
// 以下爲自定義的回調函數,當觸發監聽事件時,調用執行對應的函數
 
// ev_io事件的回調函數,當有輸入流stdin時,調用函數
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
//對ev_io事件的監控不會自動停止,需要手動在需要的時候停止
ev_io_stop (EV_A_ w);
 
//整體的loop事件在所有監控停止時停止,也可以手動關閉全部的ev_run
ev_break (EV_A_ EVBREAK_ALL);
}
 
// 時間事件的自定義回調函數,可定時觸發
static void timeout_cb (EV_P_ ev_timer *w, intrevents)
{
puts ("timeout");
//關閉最早的一個還在運行的ev_run
ev_break (EV_A_ EVBREAK_ONE);
}
 
int main (void)
{
//定義默認的 event loop,它就像一個大容器,可以裝載着很多事件不停運行
struct ev_loop *loop = EV_DEFAULT;
 
// 初始化ev_io事件監控,設置它的回調函數,和stdin
ev_io_init (&stdin_watcher, stdin_cb,/*STDIN_FILENO*/ 0, EV_READ);
//將ev_io事件放到event loop裏面運行
ev_io_start (loop, &stdin_watcher);
 
// 初始化ev_timer事件監控,設置它的回調函數,間隔時間,是否重複
ev_timer_init (&timeout_watcher, timeout_cb, 5.5,0.);
//將ev_timer事件放到event loop裏面運行
ev_timer_start (loop, &timeout_watcher);
 
// 將我們的大容器event loop整體運行起來
ev_run (loop, 0);
 
// ev_run運行結束之後,纔會運行到這裏
return 0;
}


通過上面的例子,就可以知道libev使用的思路:

首先,定義一個eventloop大容器 struct ev_loop,和想要的監控事件ev_XXX。

其次,初始化想要監控的事件,設置好回調函數和相應的參數ev_XXX_init 。

接下來,讓想要監控的事件都投身到大容器中ev_XXX_start。

最後,讓大容器帶着小容器一起運行起來 ev_run 。

 

三.常用函數詳解

1.event loop 相關

(1)從創建說起:

我們默認使用EV_DEFAULT類型的loop,使用一下語句來創建:struct ev_loop *loop = EV_DEFAULT;

EV_DEFAULT宏是以下指令:

ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV);

返回一個最基礎的ev_loop,並自動完成它的初始化,注意,如果程序中已經執行過該創建,將直接返回之前的創建。除此之外,更多自定義loop,可以使用該函數:struct ev_loop*ev_loop_new (unsigned int flags)。

(2)讓ev_loop運行起來

使用函數ev_run(loop, int flags)。

這裏解釋一下flags的作用,用於設置ev_loop的運行方式:

通常設置爲0,表示該ev_loop在所有watcher結束後停止,也可以手動break,官方鼓勵手動break。

除了0之外,還有一些選擇,如EVRUN_NOWAIT、EVRUN_ONCE。具體請看官方文檔。

ev_run函數的源碼說明。推薦博文:http://www.cnblogs.com/xiangshancuizhu/archive/2013/08/10/3250558.html

(3)ev_loop的停止

前面已經說過,在flags設置爲0的情況下,停止主要靠全部watcher停止或者手動break。

手動break用以下函數:ev_break (loop,how)

其中,how代表停止的方式:

EVBREAK_ONE:停止最久遠的那個ev_run

EVBREAK_ALL:停止所有的ev_run

2.ev_TYPE公共基礎

(1)watcher對應的幾種狀態

   initialiased:調用init函數初始化

   active:調用start進行註冊

   pending:已經觸發事件但是沒有處理

   inactive:調用stop註銷。這個狀態等同於initialised這個狀態。

(2)ev_TYPE對應不同類型的時間監控,共有的標準化函數主要如下:

typedef void (*)(struct ev_loop *loop, ev_TYPE*watcher, int revents) callback; // 回調函數都是這種類型
ev_init (ev_TYPE *watcher, callback); // 初始化watcher
ev_TYPE_set (ev_TYPE *watcher, [args]); // 設置watcher
ev_TYPE_init (ev_TYPE *watcher, callback, [args]); // 這個函數綜合了前兩個函數功能
ev_TYPE_start (loop, ev_TYPE *watcher); // 在ev_loop中註冊watcher
ev_TYPE_stop (loop, ev_TYPE *watcher); // 在ev_loop中註銷watcher
ev_set_priority (ev_TYPE *watcher, int priority); // 設置watcher優先級,值域爲[-2,2],大而優先
ev_feed_event (loop, ev_TYPE *watcher, int revents);// 這個做跨線程通知非常有用,相當於觸發了某個事件。
bool ev_is_active (ev_TYPE *watcher); // watcher是否active.
bool ev_is_pending (ev_TYPE *watcher); // watcher是否pending.
int ev_clear_pending (loop, ev_TYPE *watcher); // 清除watcher pending狀態並且返回事件

3.ev_io相關

(1)ev_io結構

ev_io用來監聽io事件,當有標準輸入或輸出時,則會觸發事件,執行回調函數。

typedef structev_io
{
EV_WATCHER_LIST (ev_io)
int fd; /* 所監聽io事件的文件描述符(file descriptor,非負數) */
int events; /*所監聽的事件,包括EV_READ, EV_WRITE 或 EV_READ | EV_WRITE */
} ev_io;

(2)初始化

兩種方式:

方式一:ev_init(ev_TYPE *watcher, callback) ,ev_io_set(ev_io *ev, intfd, int events)

方式二:ev_io_init(ev_io*ev, callback,int fd, int events)

即需要設置監聽watcher,回調函數callback,文件描述符fd,監聽的事件events。

(3)運行和停止

ev_io_start(EV_P_ev_io * w):綁定到ev_loop上

ev_io_stop(EV_P_ev_io * w):從ev_loop上撤離

4.ev_timer相關

(1)ev_timer結構

ev_timer是一個相對時間的定時器,會在給定的時間點觸發事件,還可以在固定的時間間隔之後再次觸發超時事件。

typedef structev_timer
{
int active;
int pending;
int priority; /*優先級 */
void *data; /* 參數*/
void (*cb)(struct ev_loop *loop, struct ev_timer *w,int revents); /*回調函數 */
ev_tstamp at; /* 定時器時間,單位爲s*/
ev_tstamp repeat; /*是否重複 */
} ev_timer;

(2)初始化

同樣兩種方式:

方式一:ev_init(ev_TYPE *watcher, callback) ,ev_timer_set (ev_timer *,ev_tstamp after, ev_tstamp repeat)

方式二:ev_timer_init(ev_timer *, callback, ev_tstamp after, ev_tstamp repeat)

即需要設置監聽watcher,回調函數callback,定時器時間after,是否重複repeat。

其中,repeat設置爲0表示定時器只觸發一次,設置爲正整數則間隔after秒一直不斷觸發。

(3)運行和停止

ev_timer_start(EV_P_ev_io * w):綁定到ev_loop上

ev_timer_stop(EV_P_ev_io * w):從ev_loop上撤離

(4)其它函數

ev_timer_again(loop, ev_timer *) :重啓,對於不同狀態的timer效果不同。

ev_tstampev_timer_remaining (loop, ev_timer *):返回現在到下一次觸發定時器之間的時間。

四.在Socket編程的應用實例

libev用在Socket編程上有幾個好處,用ev_timer可以控制發送頻率,用ev_io可以方便地異步進行包的接收。下面的實例就實現的這個流程,直接用代碼解釋了:

double time_span = 1.0; //發送消息的時間間隔
int test_num = 5; //發送消息的次數
int send_num = 0; //已發送消息的次數
int rev_num = 0; //已收到回覆的次數
int fd = -1; //文件描述符
 
struct ev_loop *loop = EV_DEFAULT;
ev_io io_watcher;
ev_timer timer_watcher;
 
//ev_io的回調函數,用於異步接受回覆
void io_action(struct ev_loop *main_loop,ev_io*io_w,int e)
{
char buf[4096];
char *ptr = (char *)buf;
int ret = 0;
//接收消息
ret = ::recv(fd, ptr, 4096, 0);
 
std::cout << buf << "\n";
 
//接收到所有消息,停止ev_io事件
if(rev_num >= test_num){
ev_io_stop(main_loop,io_w);
}
}
 
//ev_timer的回調函數,用於定時發送消息
void timer_action(struct ev_loop *main_loop,ev_timer*time_w,int e)
{
int ret;
std::string msg_send = "send msg";
//發送消息
ret = ::send(fd, msg_send.c_str(),strlen(msg_send.c_str()), 0);
std::cout << msg_send << "\n";
 
//發送足夠數量的消息後,停止事件
if(send_num >= test_num){
ev_timer_stop(main_loop,time_w);
}
}
 
int main(int argc, char *argv[])
{
fd = socket(AF_INET, SOCK_STREAM, 0);
ret = ::connect(fd, (struct sockaddr *)&addr,sizeof(addr));
 
ev_init(&io_watcher,io_action);
ev_io_set(&io_watcher,fd,EV_READ);
 
ev_init(&timer_watcher,timer_action);
ev_timer_set(&timer_watcher,time_span,1);
 
ev_io_start(loop,&io_watcher);
ev_timer_start(loop,&timer_watcher);
 
ev_run(loop,0);
return 0;
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章