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
vim開啟hello-world。c檔案檢視示例程式碼
發現監聽埠號為:9995好,接下來我們開始libevent的奇妙之旅,使用xshell啟動兩個本地連線,
服務端:在上述路徑執行 。/hello-world
客戶端:採用nc進行訪問 nc 127。0。0。1 9995 9995為上述程式碼中指定的埠號
每有客戶端註冊時 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等),免費分享
框架學習—— 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
);
函式呼叫成功返回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 依次執行即可