libevent概述

Libevent 是一個用C語言編寫的、輕量級的開源高效能事件通知庫,主要有以下幾個亮點:事件驅動( event-driven),高效能;輕量級,專注於網路,不如 ACE 那麼臃腫龐大;原始碼相當精煉、易讀;跨平臺,支援 Windows、 Linux、 *BSD 和 Mac Os;支援多種 I/O 多路複用技術, epoll、 poll、 dev/poll、 select 和 kqueue 等;支援 I/O,定時器和訊號等事件;註冊事件優先順序。

初識libevent

只要是學習程式設計似乎都逃離不了“hello word”定律!(嗯…真香!)編譯安裝libevent原始碼之後,進入檔案sample cd /sample接下來,我們可以看到libevent官方為我們提供的demo

「linux」詳解libevent網路庫---框架的搭建

vim開啟hello-world。c檔案檢視示例程式碼

「linux」詳解libevent網路庫---框架的搭建

發現監聽埠號為:9995好,接下來我們開始libevent的奇妙之旅,使用xshell啟動兩個本地連線,

服務端:在上述路徑執行 。/hello-world

客戶端:採用nc進行訪問 nc 127。0。0。1 9995 9995為上述程式碼中指定的埠號

「linux」詳解libevent網路庫---框架的搭建

每有客戶端註冊時 server端將列印 flushed answer,同時,client端列印Hello, World!

需要C/C++ Linux伺服器架構師學習資料加qun獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享

「linux」詳解libevent網路庫---框架的搭建

框架學習—— event_base 重中之重

原始碼中有這樣一句話:

The event_base lies at the center of Libevent; every application will have one.

誇張的理解為:libevent的世界中,event_base作為萬物起源

使用 libevent 函式之前需要分配一個或者多個 event_base 結構體。每個event_base

結構體持有一個事件集合,可以檢測以 確定哪個事件是啟用的。(相當於epoll紅黑樹的

樹根

從思想上出發

我們可以把libevent專案想象為造火箭的過程,我們都只是螺絲工,那麼造火箭需要做什麼?

1。 拿出火箭殼(建立框架)

2。 造螺絲 (建立事件)

3。 擰螺絲 (新增事件)

4。 造火箭(事件迴圈)

一:掏出火箭殼 —>event_base()建立與釋放

我們開始第一步:建立一個event_base

// 建立event_base

struct event_base* event_base_new(void)

當然,作為一個優秀的c語言程式設計師(咳,我還是個菜),要在建立的同時考慮資源釋放的問題,在程式的最後我們需要 event_base_free 進行釋放(但我們不得不提前考慮)

// 釋放event_base_free

event_base_free(struct event_base* base);

12

二:造螺絲 —>event_new()建立與釋放

注:建立事件:(只針對不帶緩衝區event事件進行講解)

// 建立新事件

struct event *event_new(

struct event_base *base,

evutil_socket_t fd, - // 檔案描述符 - int **底層是對epollin與epollout的封裝**

short what,

event_callback_fn cb, // 事件的處理回撥函式

void *arg //回撥函式傳參

);

// 事件的處理回撥函式

typedef void (*event_callback_fn)(evutil_socket_t, short, void *);

// short what

#define EV_TIMEOUT 0x01 // 已淘汰(忽略)

#define EV_READ 0x02

#define EV_WRITE 0x04

#define EV_SIGNAL 0x08 //libevent封裝了訊號相關的操作 SIGNAL

#define EV_PERSIST 0x10 // 持續觸發

#define EV_ET 0x20 // 邊沿模式

在程式的最後我們需要 event_free 進行釋放(但我們不得不提前考慮)

// 建立event_free

void event_free(struct event *event);

呼叫event_new()函式之後, 新事件處於已初始化和非未決狀態 (翻譯:螺絲造好了,還沒擰到火箭時的狀態)

三:擰螺絲 —>event_add()相關函式

建立事件之後,在將其新增到 event_base 之前實際上是不能對其做任何操作的。使用event_add()將事件新增到event_base。 (將螺絲擰上去)非未決事件 -> 未決事件。

// event_add

int event_add(

struct event *ev,

const struct timeval *tv

);

「linux」詳解libevent網路庫---框架的搭建

函式呼叫成功返回0, 失敗返回-1

設定非未決(作為了解實際中少用)未決事件 -> 非未決事件。

//對已經初始化的事件呼叫 event_del()將使其成為非未決和非啟用的。如果事件不是未決的或者啟用的,呼叫將沒有效果。

int event_del(struct event *ev);

//成功時函式返回 0,失敗時返回-1。

有關於未決態和非未決態的理解:

1。 非未決: 沒有資格被處理

2。 未決: 有資格被處理但是還沒有處理

四:一節一節造火箭 —>event_base_dispatch()相關函式

最後,我們只需將新增 事件迴圈使用event_base_dispatch()函式

// event_base_dispatch(簡化版event_base_loop())

int event_base_dispatch(struct event_base* base);

//等同於沒有設定標誌的 event_base_loop ( )

//將一直執行,直到沒有已經註冊的事件了,或者呼叫 了event_base_loopbreak()或者 event_base_loopexit()為止

關於event_base_loop()函式

// event_base_loop()

int event_base_loop(struct event_base *base, int flags);

//正常退出返回0, 失敗返回-1

//flages

#define EVLOOP_ONCE 0x01

//事件只會被觸發一次

//事件沒有被觸發, 阻塞等

#define EVLOOP_NONBLOCK 0x02

//非阻塞 等方式去做事件檢測

//不關心事件是否被觸發了

#define EVLOOP_NO_EXIT_ON_EMPTY 0x04

//沒有事件的時候, 也不退出輪詢檢測

關於退出事件迴圈函式event_base_loopexit()與event_base_loopbreak():

執行當前後退出 event_base_loopexit()

//如果 event_base 當前正在執行啟用事件的回撥 ,它將在執行完當前正在處理的事件後立即退出

int event_base_loopexit(

struct event_base *base,

const struct timeval *tv

);

//引數struct timeval *tv

struct timeval {

long tv_sec;

long tv_usec;

};

立即退出迴圈 event_base_loopbreak()

//讓event_base 立即退出迴圈

int event_base_loopbreak(struct event_base *base);

//返回值: 成功 0, 失敗 -1

Demo

最後舉個栗子:注:用不帶緩衝區操作時的寫法:採用

fifo

通訊的方式

//write_fifo。c

#include

#include

#include

#include

#include

#include

#include

#include

// 回撥函式

void write_cb(evutil_socket_t fd, short what, void *arg)

{

// write管道

char buf[1024] = {0};

static int num = 666;

sprintf(buf, “hello, world == %d\n”, num);

write(fd, buf, strlen(buf)+1);

}

int main(int argc, const char* argv[])

{

// open file

int fd = open(“myfifo”, O_WRONLY | O_NONBLOCK);

if(fd == -1)

{

perror(“open error”);

exit(1);

}

// 寫管道

struct event_base* base = NULL;

base = event_base_new();

// 建立事件

struct event* ev = NULL;

// 檢測的寫緩衝區是否有空間寫

ev = event_new(base, fd, EV_WRITE , write_cb, NULL);

// 新增事件

event_add(ev, NULL);

// 事件迴圈

event_base_dispatch(base);

// 釋放資源

event_free(ev);

event_base_free(base);

close(fd);

return 0;

}

//read_fifo

#include

#include

#include

#include

#include

#include

#include

#include

// 讀取回調函式

void read_cb(evutil_socket_t fd, short what, void *arg)

{

// 讀管道

char buf[1024] = {0};

int len = read(fd, buf, sizeof(buf));

printf(“data len = %d, buf = %s\n”, len, buf);

printf(“read event: %s”, what & EV_READ ? “Yes” : “No”);//對what型別對判斷

}

int main(int argc, const char* argv[])

{

unlink(“myfifo”);

//建立有名管道

mkfifo(“myfifo”, 0664);

// open file

int fd = open(“myfifo”, O_RDONLY | O_NONBLOCK);

if(fd == -1)

{

perror(“open error”);

exit(1);

}

// 讀管道

struct event_base* base = NULL;

base = event_base_new();

// 建立事件

struct event* ev = NULL;

ev = event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);

// 新增事件

event_add(ev, NULL);

// 事件迴圈

event_base_dispatch(base);

// 釋放資源

event_free(ev);

event_base_free(base);

close(fd);

return 0;

}

使用gcc對檔案編譯 -levevt引用libevent庫

gcc read_fifo。c -o read -levent

gcc write_fifo。c -o write -levent

。/read 。/write 依次執行即可