歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux管理 >> Linux服務

使用libevent編寫Linux服務

libevent簡介

libevent是一個支持Windowslinuxbsd等平台的網絡事件驅動程序庫。它支持多種I/O服用機制,按照優先級從高到低依次為:evportkqueueepolldevpollrtsigpollselect。它可根據操作系統,按照優先級從高到底自主選擇驅動。

     用戶可以通過http://www.monkey.org/~provos/libevent/來獲取libevent的源碼、libevent出現的背景、以及其他一些詳細資料。

libevent的使用

1 初始化事件  

我們首先完成對libenvent的事件初始化和事件驅動模型的選擇。(在使用多線程的情況下,一般我們需獲取所返回的事件根基)

 

main_base = event_init();

  

event_init函數返回的是一個event_base對象,該對象包括了事件處理過程中的一些全局變量,其結構為:

struct event_base {
    const struct eventop *evsel;
    void *evbase;
    int event_count;        /* counts number of total events */
    int event_count_active; /* counts number of active events */

    int event_gotterm;      /* Set to terminate loop */
    int event_break;        /* Set to terminate loop immediately */

    /* active event management */
    struct event_list **activequeues;
    int nactivequeues;

    /* signal handling info */
    struct evsignal_info sig;

    struct event_list eventqueue;
    struct timeval event_tv;

    struct min_heap timeheap;

    struct timeval tv_cache;
};


2 添加事件

        在事件初始化完畢後,我們可以使用event_set設置事件,然後使用event_add將其加入。

   這裡我們首先完成socket的監聽,然後將其加入的事件隊列中(這裡我們對所有的異常都不做考慮)。

1socket監聽

struct sockaddr_in listen_addr;

int port = 10000; //socket監聽端口

int listen_fd = socket(AF_INET, SOCK_STREAM, 0);

memset(&listen_addr, 0, sizeof(listen_addr));

listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons(port)

reuseaddr_on = 1;

setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)) #支持端口復用

bind(listen_fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr))'

listen(listen_fd, 1024);

/*將描述符設置為非阻塞*/

int flags = fcntl(listen_fd,, F_GETFL);

flags |= O_NONBLOCK;

fcntl(listen_fd, F_SETFL, flags);


2)事件設置

      socket服務建立後,就可以進行事件設置。我們使用event_set來設置事件對象,其傳入參數包括事件根基(event_base對象),描述符,事件類型,事件發生時的回調函數,回調函數傳入參數。其中事件類型包括EV_READEV_WRITEEV_PERSISTEV_PERSIST和前兩者結合使用,表示該事件為持續事件。

struct event ev;

event_set(&ev, listen_fd, EV_READ | EV_PERSIST, accept_handle, (void *)&ev);

3)���件添加與刪除

         事件設置好後,就可以將其加入事件隊列。event_add用來將事件加入,它接受兩個參數:要添加的事件和時間的超時值。 如果需要將事件刪除,可以使用event_del來完成。event_del函數會取消所指定的事件。

event_add(&ev, NULL)

3 進入事件循環

    事件成功添加後就是萬事具備只欠東風了,libevent提供了多種方式來進入事件循環,我個人常用的是event_dispatchevent_base_loop,前者最後實際是使用當前事件根基來調用event_base_loop

   event_base_loop(main_base, 0);

4 處理連接

     到這裡為止,大家已經完成了事件的設置、事件的添加並進入到了事件循環。但是當事件發生時(這裡就是連接建立)如何處理呢? 聰明的用戶會想到前面我們在事件設置時指定的回調函數accept_handle。沒錯,當連接建立時回調函數accept_handle會自動的得到調用。

       對於緩沖區的讀寫在非阻塞式網絡編程中是一個難以處理的問題,幸運的是libevent提供了buffereventevbuf來替我們完成該項工作。這裡我們采用bufferevent來處理。

(1)生成bufferevent對象

      使用bufferevent_new對象來生成bufferevent對象,並分別指定讀、寫、連接錯誤時的處理函數和函數傳入參數。

(2) 設置讀取量

bufferevent的讀事件激活以後,即使用戶沒有讀取完bufferevent緩沖區中的數據, bufferevent讀事件也不會再次被激活。因為bufferevent的讀事件是由其所監控的描述符的讀事件激活的,只有描述符可讀,讀事件才會被激活。可通過設置wm_read.high來控制bufferevent從描述符緩沖區中讀取的數據量。

(3) 將事件加入事件隊列

和前面一樣,在事件設置好後,需將事件加入到事件隊列中, 不過bufferevent的有自己專門的加入函數bufferevent_base_set和激活函數bufferevent_enable

bufferevent接收兩個參數事件根基個事件對象,前者用來指定事件將加入到哪個事件根基中,後者說明需將那個bufferevnet事件加入。(在多線程的情況下,每個線程可能有自己單獨的事件根基)

bufferevent初始化完畢後,可以使用bufferevent_enablebufferevent_disable反復的激活與禁止事件,其接收參數為事件對象和事件標志。其中標志參數為EV_READEV_WRITE

 

void accept_handle(const int sfd, const short event, void *arg)
{

   struct sockaddr_in addr;

   socklen_t addrlen = sizeof(addr);

   int fd = accept(sfd, (struct sockaddr *) &addr, &addrlen); //處理連接

    buf_ev = bufferevent_new(fd,   buffered_on_read, NULL, NULL, fd)

    buf_ev->wm_read.high = 4096

    bufferevent_base_set(main_base, buf_ev);

    bufferevent_enable(buf_ev, EV_READ);

}

5 讀取緩沖區

當緩沖區讀就緒時會自動激活前面注冊的緩沖區讀函數,我們可以使用bufferevent_read函數來讀取緩沖區

bufferevent_read函數參數分別為:所需讀取的事件緩沖區,讀入數據的存放地,希望讀取的字節數。函數返回實際讀取的字節數。

注意:及時緩沖區未讀完,事件也不會再次被激活(除非再次有數據)。因此此處需反復讀取直到全部讀取完畢。

6 寫回客戶端

    bufferevent系列函數不但支持讀取緩沖區,而且支持寫緩沖區(即將結果返回給客戶端)。

 

void buffered_on_read(struct bufferevent *bev, void * arg){

    char buffer[4096]

    ret = bufferevent_read(bev, &buffer, 4096);

    bufferevent_write(bef, (void *)&buffer, 4096);

}

 

結束語

    至此我們已經可以使用libevent編寫非阻塞的事件驅動服務器,它支持連接建立、socket可讀等事件的處理。

但在實際的使用事件驅動的服務器中,通常是使用一個線程處理連接,然後使用多個線程來處理請求。後面我將繼續介紹如何使用libevent來編寫多線程的服務器。

Copyright © Linux教程網 All Rights Reserved