Libevent初探
檢查L(zhǎng)ibevent支持的IO復(fù)用方法
Libevent作為一個(gè)高性能網(wǎng)絡(luò)庫(kù),內(nèi)部封裝了多種IO復(fù)用技術(shù),如果想看下Libevent在當(dāng)前系統(tǒng)下支持哪些IO復(fù)用技術(shù)呢? int main(int argc, char **argv) {// 版本信息cout << event_get_version() << endl;// 所支持的IO復(fù)用方法const char **methods = event_get_supported_methods();for (int i = 0; methods[i] != NULL; i++) {cout << methods[i] << endl;}return 0; }輸出結(jié)果為:(Centos7 Clion 2016.1.3環(huán)境)
event_get_supported_methods()函數(shù)返回Libevent支持的IO復(fù)用方法名稱(chēng)數(shù)組,以NULL結(jié)尾。該函數(shù)實(shí)際返回的是全局變量eventops數(shù)組,eventops數(shù)組存放的是所有支持的IO復(fù)用函數(shù),eventops聲明部分的代碼如下:
/* Array of backends in order of preference. */ /* Libevent通過(guò)遍歷eventops數(shù)組來(lái)選擇其后端IO復(fù)用技術(shù),遍歷的順序是從數(shù)組的第一個(gè)元素開(kāi)始,* 到最后一個(gè)元素結(jié)束。Linux系統(tǒng)下,默認(rèn)選擇的后端IO復(fù)用技術(shù)是epoll。*/ static const struct eventop *eventops[] = { #ifdef _EVENT_HAVE_EVENT_PORTS&evportops, #endif #ifdef _EVENT_HAVE_WORKING_KQUEUE&kqops, #endif #ifdef _EVENT_HAVE_EPOLL&epollops, #endif #ifdef _EVENT_HAVE_DEVPOLL&devpollops, #endif #ifdef _EVENT_HAVE_POLL&pollops, #endif #ifdef _EVENT_HAVE_SELECT&selectops, #endif #ifdef WIN32&win32ops, #endifNULL };Libevent是如何打日志的
libevent的錯(cuò)誤處理底層調(diào)用的是va_start/va_end等相關(guān)宏,它們所在的頭文件是<stdarg.h>,使用C函數(shù)庫(kù)提供的這些函數(shù),我們也可以實(shí)現(xiàn)一個(gè)自己的打日志程序,以下是一個(gè)使用va_start/va_end的測(cè)試程序: void log(const char *fmt, ...) {char buff[512];va_list ap;va_start(ap, fmt);int len = vsnprintf(buff, sizeof(buff), fmt, ap);buff[len] = '\0';va_end(ap);cout << buff << endl; }- va_start:宏定義,引用最后一個(gè)固定參數(shù)所以它能夠?qū)勺儏?shù)進(jìn)行定位。
- va_end:宏定義,函數(shù)返回之前一定要調(diào)用va_end,這是因?yàn)槟承?shí)現(xiàn)在函數(shù)返回之前需要調(diào)整控制信息。
更多va_start/va_end信息請(qǐng)點(diǎn)擊:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html。
定時(shí)器的使用
#include <iostream>#include <event.h> #include <event2/http.h>using namespace std;// Time callback function void onTime(int sock, short event, void *arg) {static int cnt = 0;cout << "Game Over! " << cnt++ << endl;struct timeval tv;tv.tv_sec = 1;tv.tv_usec = 0;if (cnt < 5) {// Add timer eventevent_add((struct event *) arg, &tv);}else {cout << "onTime is over" << endl;} }int main(int argc, char **argv) {cout << event_get_version() << endl;struct event_base *base = event_init();struct event ev;evtimer_set(&ev, onTime, &ev);struct timeval timeevent;timeevent.tv_sec = 1;timeevent.tv_usec = 0;event_add(&ev, &timeevent);// Start event loopevent_base_dispatch(base);event_base_free(base);return 0; }輸出結(jié)果如下:
LZ安裝的是Libevent版本是2.0版本,event_init()函數(shù)初始化一個(gè)事件類(lèi)結(jié)構(gòu)體,其中已經(jīng)選擇好了IO復(fù)用函數(shù),比如Linux下一般是epoll;初始化了一個(gè)事件活動(dòng)隊(duì)列,當(dāng)事件發(fā)生時(shí),會(huì)被加入到該事件活動(dòng)隊(duì)列中,然后統(tǒng)一執(zhí)行事件活動(dòng)隊(duì)列中的所有事件(也就是調(diào)用對(duì)應(yīng)的回調(diào)函數(shù))。event_base結(jié)構(gòu)體詳細(xì)內(nèi)容如下:
/* 結(jié)構(gòu)體event_base是Libevent的Reactor */ struct event_base {/* 初始化Reactor時(shí)選擇的一種后端IO復(fù)用機(jī)制,并記錄在如下字段中 */const struct eventop *evsel;/* 指向IO復(fù)用機(jī)制真正存儲(chǔ)的數(shù)據(jù),它通過(guò)evsel成員的init函數(shù)來(lái)進(jìn)行初始化 */void *evbase;/* 事件變化隊(duì)列,其用途是:如果一個(gè)文件描述符上注冊(cè)的事件被多次修改,則可以使用緩沖區(qū)來(lái)避免重復(fù)的* 系統(tǒng)調(diào)用(比如epoll_wait)。它僅能用于時(shí)間復(fù)雜度為O(1)的IO復(fù)用技術(shù) */struct event_changelist changelist;/* 指向信號(hào)的后端處理機(jī)制,目前僅在singal.h文件中定義了一種處理方法 */const struct eventop *evsigsel;/* 信號(hào)事件處理器使用的數(shù)據(jù)結(jié)構(gòu),其中封裝了一個(gè)由socketpair創(chuàng)建的管道。它用于信號(hào)處理函數(shù)和* 事件多路分發(fā)器之間的通信 */struct evsig_info sig;/* 以下3個(gè)成員是添加到該event_base的虛擬事件、所有事件和激活事件的數(shù)量 */int virtual_event_count;int event_count;int event_count_active;/* 是否執(zhí)行完活動(dòng)事件隊(duì)列上的剩余的任務(wù)之后就退出事件處理 */int event_gotterm;/* 是否立即退出事件循環(huán),而不管是否還有任務(wù)需要處理 */int event_break;/* 是否應(yīng)該啟動(dòng)一個(gè)新的事件循環(huán) */int event_continue;/* 目前正在處理的活動(dòng)事件隊(duì)列的優(yōu)先級(jí) */int event_running_priority;/* 事件循環(huán)是否啟動(dòng) */int running_loop;/* 活動(dòng)事件隊(duì)列數(shù)組,索引值越小的隊(duì)列,優(yōu)先級(jí)越高。高優(yōu)先級(jí)的活動(dòng)事件隊(duì)列中的事件處理器將被優(yōu)先處理 */struct event_list *activequeues;/* 活動(dòng)事件隊(duì)列數(shù)組的大小,即該event_base共有nactivequeues個(gè)不同優(yōu)先級(jí)的活動(dòng)事件隊(duì)列 */int nactivequeues;/* common timeout logic *//* 以下3個(gè)成員用于管理通用定時(shí)器隊(duì)列 */struct common_timeout_list **common_timeout_queues;int n_common_timeouts;int n_common_timeouts_allocated;/* 存放延時(shí)回調(diào)函數(shù)的鏈表,事件循環(huán)每次成功處理完一個(gè)活動(dòng)事件隊(duì)列中的所有事件之后,* 就調(diào)用一次延遲回調(diào)函數(shù) */struct deferred_cb_queue defer_queue;/* 文件描述符和IO事件之間的映射關(guān)系表 */struct event_io_map io;/* 信號(hào)值和信號(hào)事件之間的映射關(guān)系表 */struct event_signal_map sigmap;/* 注冊(cè)事件隊(duì)列,存放IO事件處理器和信號(hào)事件處理器 */struct event_list eventqueue;struct timeval event_tv;/* 時(shí)間堆 */struct min_heap timeheap;struct timeval tv_cache;#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)/** Difference between internal time (maybe from clock_gettime) and* gettimeofday. */struct timeval tv_clock_diff;/** Second in which we last updated tv_clock_diff, in monotonic time. */time_t last_updated_clock_diff; #endif/* 多線程支持 */ #ifndef _EVENT_DISABLE_THREAD_SUPPORT/* threading support *//** The thread currently running the event_loop for this base *//* 當(dāng)前運(yùn)行該event_base的事件循環(huán)的線程 */unsigned long th_owner_id;/** A lock to prevent conflicting accesses to this event_base */void *th_base_lock; /* 鎖變量 *//** The event whose callback is executing right now *//* 當(dāng)前事件循環(huán)正在執(zhí)行哪個(gè)事件處理器的回調(diào)函數(shù) */struct event *current_event;/** A condition that gets signalled when we're done processing an* event with waiters on it. *//* 條件變量,用于喚醒正在等待某個(gè)事件處理完畢的線程 */void *current_event_cond;/** Number of threads blocking on current_event_cond. */int current_event_waiters; /* 等待current_event_cond的線程數(shù) */ #endif/** Flags that this base was configured with *//* 該vent_base的一些配置參數(shù) */enum event_base_config_flag flags;/* 下面這些成員變量給工作線程喚醒主線程提供了方法(使用socketpair創(chuàng)建的管道) *//* Notify main thread to wake up break, etc. *//** True if the base already has a pending notify, and we don't need* to add any more. */int is_notify_pending;/** A socketpair used by some th_notify functions to wake up the main* thread. */evutil_socket_t th_notify_fd[2];/** An event used by some th_notify functions to wake up the main* thread. */struct event th_notify;/** A function used to wake up the main thread from another thread. */int (*th_notify_fn)(struct event_base *base); }event_add()函數(shù)是往事件結(jié)構(gòu)體中加入監(jiān)聽(tīng)的一個(gè)事件,這里是定時(shí)事件,當(dāng)定時(shí)事件到時(shí),就會(huì)執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。event_base_dispatch()函數(shù)開(kāi)始執(zhí)行事件監(jiān)聽(tīng),對(duì)應(yīng)于epoll的話(huà)也就是調(diào)用epoll_wait了。最后,當(dāng)程序執(zhí)行完畢后,需要調(diào)用event_base_free()函數(shù)來(lái)執(zhí)行資源的銷(xiāo)毀操作,至此,整個(gè)定時(shí)器事件就執(zhí)行完畢了。
?
簡(jiǎn)單的HTTP服務(wù)器
使用Libevent,我們可以用不超過(guò)50行代碼實(shí)現(xiàn)一個(gè)簡(jiǎn)單的HTTP服務(wù)器程序,沒(méi)有聽(tīng)錯(cuò),就是幾十行代碼,不像Java那樣,需要配置Tomcat,然后編寫(xiě)對(duì)應(yīng)的Servet,配置web.xml等等(如果使用SSM或者SSH的話(huà)步驟或許更多一點(diǎn)呦 :( )。下面就是一個(gè)簡(jiǎn)單的HTTP服務(wù)器示例代碼:
#include <iostream>#include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h>using namespace std;#define INFO 1 #define ERR 3 static void log(int level, string info) {switch (level) {case INFO:cout << "[info] tid[" << pthread_self() << "]: " << info << endl;break;case ERR:cout << "[err] tid[" << pthread_self() << "]: " << info << endl;break;default:break;} }/*** http callback function*/ void httpHandler(struct evhttp_request *request, void *arg) {struct evbuffer *buff = evbuffer_new();if (!buff) {log(INFO, "evbuffer_new error");return;}evbuffer_add_printf(buff, "Hello world</br>");evbuffer_add_printf(buff, "Server Responsed.</br> Requested: %s<br/>", evhttp_request_get_uri(request));evbuffer_add_printf(buff, " Host: %s<br/>", evhttp_request_get_host(request));evbuffer_add_printf(buff, " Command: %d", evhttp_request_get_command(request));evhttp_send_reply(request, HTTP_OK, "OK", buff);evbuffer_free(buff); }int main(int argc, char **argv) {struct event_base *base = event_base_new();struct evhttp *httpServer = evhttp_new(base);int result = evhttp_bind_socket(httpServer, NULL, 8080);if (result != 0) {log(ERR, "evhttp_bind_socket error");return -1;}/* 這是http回調(diào)函數(shù) */evhttp_set_gencb(httpServer, httpHandler, NULL);cout << "Http server start OK..." << endl;event_base_dispatch(base);evhttp_free(httpServer);event_base_free(base);return 0; }訪問(wèn)頁(yè)面如下,192.168.1.150主機(jī)是linux服務(wù)器。
看到這里,學(xué)習(xí)Java Web的小伙伴是不是覺(jué)得很熟悉,沒(méi)錯(cuò),就是像Servlet。LZ個(gè)人覺(jué)得,對(duì)于小型程序來(lái)說(shuō),使用C/C++的網(wǎng)絡(luò)庫(kù)編程程序更爽一點(diǎn),因?yàn)楦?#34;接地氣?"一點(diǎn),也就操作起來(lái)更加靈活,使用Java的話(huà)肯定要使用Servet容器了,比如Tomcat或者Jboss等,然后各種配置等。但是對(duì)于動(dòng)態(tài)Web技術(shù)來(lái)說(shuō),使用Java更爽一點(diǎn)。
參考資料 1、libevent-百度百科 2、Libevent部分源碼轉(zhuǎn)載于:https://www.cnblogs.com/luoxn28/p/5813860.html
總結(jié)
以上是生活随笔為你收集整理的Libevent初探的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 软件与程序
- 下一篇: 【腾讯Bugly干货分享】那些年,我们一