天天看點

libev使用簡述

官方示例

// libev需要的頭檔案
#include <ev.h>
#include <stdio.h>
 
// 建立需要監聽的事件,這些事件類型是libev提供的
//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,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使用簡述

監聽了     控制台輸入事件和定時器事件

如果5.5s内沒輸入,列印timeout

控制台輸入了,列印stdin ready

使用的思路:

  • 建立

    ev_xxx

    監控器類型
  • ev_xxx_init注冊該監控器感興趣的事件,設定它的回調函數和其他參數
  • ev_xxx_start把該監控器放入事件驅動容器
  • ev_run事件驅動容器運作

向libev注冊感興趣的events,比如Socket事件,libev會對所注冊的事件進行管理,并在事件發生時觸發相應的程式

libev實作簡單服務端

監聽到客服端連接配接事件  會初始化通信socket,列印someone connected,然後注冊資料接收事件

監聽到資料接收事件,會轉發該資料給客服端

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <netinet/in.h>
#include <strings.h>
#include <ev.h>


#define PORT 8333

#define BUFFER_SIZE 1024

//與客服端資料交換
void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    char buffer[BUFFER_SIZE];
    ssize_t read;

    if(EV_ERROR & revents)
    {
        printf("error event in read");
        return;
    }

    read = recv(watcher->fd, buffer, BUFFER_SIZE, 0);

    if(read < 0)
    {
        printf("read error,errno:%d\n", errno);
        return;
    }
    else if(0 == read)
    {
        printf("someone disconnected.errno:%d\n", errno);
        ev_io_stop(loop,watcher);//停止
        free(watcher);
        return;
    }
    else
    {
        printf("get the message:%s\n", buffer);
    }

    send(watcher->fd, buffer, read, 0);
    bzero(buffer, read);
}

//初始化通信socket,成功就列印"someone connected"
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int client_sd;
    struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));

    if(EV_ERROR & revents)
    {
        printf("error event in accept\n");
        return;
    }

    client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
    if (client_sd < 0)
    {
        printf("accept error\n");
        return;
    }


    printf("someone connected \n");

    ev_io_init(w_client, read_cb, client_sd, EV_READ);//socket事件監控器注冊了資料讀取事件
    ev_io_start(loop, w_client);
}

int main()
{
    int sd = -1;
    struct sockaddr_in addr;

    if((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket error. errno:%d\n", errno);
        return -1;
    }

    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);//#define PORT 8333
    addr.sin_addr.s_addr = INADDR_ANY;

    if(bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
    {
        printf("bind error.errno:%d\n",errno);
        return -1;
    }

    if(listen(sd, 5) < 0)
    {
        printf("listen error\n");
        return -1;
    }

    
    //上面是監聽socket初始化

     struct ev_loop *loop = ev_default_loop(0);//初始化事件驅動池
     struct ev_io socket_watcher;//socket事件監控器

    ev_io_init(&socket_watcher, accept_cb, sd, EV_READ);//EV_READ是監聽的事件,連接配接事件發生,回調函數accept_cb處理
    ev_io_start(loop, &socket_watcher);//放入事件驅動池


    while (1)
    {
        printf("ev_loop working\n") ;
        ev_loop(loop, 0);//一次ev_loop執行一輪事件處理 ,  while (1)實作事件循環處理
    }
    return 0;
}
           

服務端接收了資料并轉發給用戶端

libev使用簡述

 用戶端接收的

libev使用簡述

使用原理

libev通過各種事件監聽器watcher對各種類型的事件進行監聽

watcher是libev定義的結構體

事件是libev定義的宏

ev_loop的建立,運作,停止

//預設使用EV_DEFAULT類型的loop
struct ev_loop *loop = EV_DEFAULT;
傳回一個最基礎的ev_loop,并自動完成它的初始化
如果程式中已經執行過該建立,将直接傳回之前的建立

也可以用ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV);
struct ev_loop *loop = ev_default_loop(0);



           
ev_run(loop, int flags);
flags的作用,用于設定ev_loop的運作方式
通常設定為0,表示該ev_loop在所有watcher結束後停止,也可以手動break


ev_break (loop,how)
how代表停止的方式:
EVBREAK_ONE:停止最久遠的那個ev_run
EVBREAK_ALL:停止所有的ev_run

           

watcher結構體和共有的标準化函數

用面向對象的思想去了解,以下是"基類"

typedef struct ev_watcher
{
    int active; //是否注冊到容器中
    int pending; //已經觸發事件但是沒有處理
    int priority;
    void *data;	
    void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);
} ev_watcher;
           

舉例:"派生類"ev_io

typedef struct ev_io
{
    int active; 
    int pending;
    int priority;
    void *data;	
    void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);
    struct ev_watcher_list *next;//裝監控器的List,與基類配套

    int fd; //這裡的fd,events就是派生類的私有成員,分别表示監聽的檔案fd和觸發的事件(可讀還是可寫) 
    int events; 
} ev_io;

為了對成員變量使用的語句進行簡化,就又寫了一個"ev_wrap.c"  也可以這樣
typedef structev_io
{
EV_WATCHER_LIST (ev_io)
int fd; 
int events; 
} ev_io;
           
//共有的标準化函數
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狀态并且傳回事件
           

繼續閱讀