一、概述
1.特點:
1.事件驅動、高性能、輕量級、專注於網絡
2.源代碼精煉、易讀
3.跨平台
4.支持多種I/O多路復用技術,如epoll 、poll 、select等
5.支持I/O和信號等事件
2.使用libevent 函數之前需要分配一個或者多個 event_base 結構體, 每個event_base結構體持有一個事件集合, 可以檢測以確定哪個事件是激活的, event_base結構相當於epoll紅黑樹的樹根節點, 每個event_base都有一種用於檢測某種事件已經就緒的 “方法”(回調函數)
通常情況下可以通過event_base_new函數獲得event_base結構。
3.相關函數
1 struct event_base *event_base_new(void); 函數說明: 獲得event_base結構 參數說明: 無 返回值: 成功返回event_base結構體指針; 失敗返回NULL; 2 void event_base_free(struct event_base *); 函數說明: 釋放event_base指針 3 int event_reinit(struct event_base *base); 函數說明: 如果有子進程, 且子進程也要使用base, 則子進程需要對event_base重新初始化, 此時需要調用event_reinit函數. 函數參數: 由event_base_new返回的執行event_base結構的指針 返回值: 成功返回0, 失敗返回-1 對於不同系統而言, event_base就是調用不同的多路IO接口去判斷事件是否已經被激活, 對於linux系統而言, 核心調用的就是epoll, 同時支持poll和select. 查看libevent支持的后端的方法有哪些: const char **event_get_supported_methods(void); 函數說明: 獲得當前系統(或者稱為平台)支持的方法有哪些 參數: 無 返回值: 返回二維數組, 類似與main函數的第二個參數**argv. const char * event_base_get_method(const struct event_base *base); 函數說明: 獲得當前base節點使用的多路io方法 函數參數: event_base結構的base指針. 返回值: 獲得當前base節點使用的多路io方法的指針 libevent在地基打好之后, 需要等待事件的產生, 也就是等待事件被激活, 所以程序不能退出, 對於epoll來說, 我們需要自己控制循環, 而在libevent中也給我們提供了API接口, 類似while(1)的功能. int event_base_dispatch(struct event_base *base); // 函數說明: 進入循環等待事件 參數說明:由event_base_new函數返回的指向event_base結構的指針 調用該函數, 相當於沒有設置標志位的event_base_loop。程序將會一直運行, 直到沒有需要檢測的事件了, 或者被結束循環的API終止。 int event_base_loopexit(struct event_base *base, const struct timeval *tv); int event_base_loopbreak(struct event_base *base); struct timeval { long tv_sec; long tv_usec; }; 兩個函數的區別是如果正在執行激活事件的回調函數, 那么event_base_loopexit將在事件回調執行結束后終止循環(如果tv時間非NULL, 那么將等待tv設置的時間后立即結束循環), 而event_base_loopbreak會立即終止循環。 typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg); struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg); 函數說明: event_new負責創建event結構指針, 同時指定對應的地基base, 還有對應的文件描述符, 事件, 以及回調函數和回調函數的參數。 參數說明: base: 對應的根節點--地基 fd: 要監聽的文件描述符 events:要監聽的事件 #define EV_TIMEOUT 0x01 //超時事件 #define EV_READ 0x02 //讀事件 #define EV_WRITE 0x04 //寫事件 #define EV_SIGNAL 0x08 //信號事件 #define EV_PERSIST 0x10 //周期性觸發 #define EV_ET 0x20 //邊緣觸發, 如果底層模型支持設置 則有效, 若不支持則無效. 若要想設置持續的讀事件則: EV_READ | EV_PERSIST cb 回調函數, 原型如下: typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void *arg); 注意: 回調函數的參數就對應於event_new函數的fd, event和arg #define evsignal_new(b, x, cb, arg) \ event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) int event_add(struct event *ev, const struct timeval *timeout); 函數說明: 將非未決態事件轉為未決態, 相當於調用epoll_ctl函數(EPOLL_CTL_ADD), 開始監聽事件是否產生, 相當於epoll的上樹操作. 參數說明: ev: 調用event_new創建的事件 timeout: 限時等待事件的產生, 也可以設置為NULL, 沒有限時。 int event_del(struct event *ev); 函數說明: 將事件從未決態變為非未決態, 相當於epoll的下樹(epoll_ctl調用 EPOLL_CTL_DEL操作)操作。 參數說明: ev指的是由event_new創建的事件. void event_free(struct event *ev); 函數說明: 釋放由event_new申請的event節點。
二、示例代碼
//編寫libevent服務端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <event2/event.h> struct event *connev = NULL; void readcb(evutil_socket_t fd,short events,void *arg){ int n; char buf[1024]; memset(buf,0x00,sizeof(buf)); n = read(fd,buf,sizeof(buf)); if(n<=0){ close(fd); //將通訊文件描述符對應的事件從base地基上刪除 event_del(connev); }else{ write(fd,buf,n); } } //創建連接回調事件 void conncb(evutil_socket_t fd,short events ,void *arg){ struct event_base *base = (struct event_base*)arg; //接收新的客戶端連接 int cfd = accept(fd,NULL,NULL); if(cfd>0){ //創建通信文件描述對應的事件並設置回調函數為readcb connev = event_new(base,cfd,EV_READ|EV_PERSIST,readcb,NULL); if(connev==NULL){ //退出循環 event_base_loopexit(base,NULL); } //將通信文件描述符對應的事件上event_base地基 event_add(connev,NULL); } } int main(int argc, char const *argv[]) { //1.創建socket int lfd = socket(AF_INET,SOCK_STREAM,0); //2.設置端口復用 int opt; setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); //3.綁定 struct sockaddr_in serv; bzero(&serv,sizeof(serv)); serv.sin_addr.s_addr = htonl(INADDR_ANY); serv.sin_port = htons(8888); serv.sin_family = AF_INET; bind(lfd,(struct sockaddr *)&serv,sizeof(serv)); //4.監聽 listen(lfd,128); //5.創建地基 struct event_base *base = event_base_new(); if(base==NULL){ perror("event_base_new error"); return -1; } //6.創建文件描述符對應的事件 struct event *ev = event_new(base,lfd,EV_READ|EV_PERSIST,conncb,base); if(ev==NULL){ perror("event_new error"); return -1; } //7.將新的事件節點上base地基 event_add(ev,NULL); //8.進行事件分發(進入事件循環等待) event_base_dispatch(base); //9.釋放資源 event_base_free(base); event_free(ev); close(lfd); return 0; }