随着nginx,nodejs,Lua等技术大放异彩的时候,我们的程序设计习惯也在不断地改变中,现在什么都需要“异步”,做不到异步就和别人有差距了。看来教材上基于多进程多线程的模型等等远远不能满足现在互联网项目的需求。
我们知道不同操作系统,不同底层内核对于IO的事件驱动模型是不一样的,有poll,select,windows select,epoll,kqueue等多种选择。nginx服务器对于这些IO多路选择都做了很好的封装,我看libevent库也是对它们做了封装,就先来使用一下libevent库,感受它的事件触发的魅力。
首先是创建一个“事件”的方式:
#include<event2\event.h> #include<stdio.h> #pragma comment(lib,"ws2_32.lib") static void discard_log(int severity, const char *msg) { //自定义记录日志 } int main(int argc, wchar_t* argv[]) { //替换记录日志的默认函数 event_set_log_callback(discard_log); //设置一个新的事件,设置配置信息 struct event_base *base; struct event_config *config = event_config_new(); //可供选择的方法有 select,poll,epoll,kqueue,devpoll,evport,win32 // //这里,不要使用kqueue event_config_avoid_method(config, "kqueue"); //特征值有:EV_FEATURE_ET,EV_FEATURE_O1,EV_FEATURE_FDS // //这里,需要支持边沿触发的后端 event_config_require_features(config, EV_FEATURE_ET); //选项有:EVENT_BASE_FLAG_NOLOCK,EVENT_BASE_FLAG_IGNORE_ENV, // EVENT_BASE_FLAG_STARTUP_IOCP,EVENT_BASE_FLAG_NO_CACHE_TIME // //这里,不要分配锁 event_config_set_flag(config, EVENT_BASE_FLAG_NOLOCK); base = event_base_new_with_config(config); event_config_free(config); if (base) { //... } else { return -1; } event_base_free(base); return 0; }
下面是创建“event”的代码,我这里用“键盘输入”作为监听的描述符(方便演示),当程序运行时如果不输入内容,则等待五秒超时会触发cb_func函数,如果5秒内有键盘输入且回车,则描述符可读立即触发cb_func函数。
#include<event.h> #include<stdio.h> //事件触发的回调函数 void cb_func(evutil_socket_t fd, short what, void *arg) { const char *data = (const char *)arg; printf("Got an event on socket %d:%s%s%s [%s]", (int) fd, (what&EV_TIMEOUT) ? " timeout" : "", (what&EV_READ) ? " read" : "", (what&EV_SIGNAL) ? " signal" : "", data); } void main_loop(evutil_socket_t fd) { struct event *ev; struct timeval five_seconds = {5,0};//超时5秒 struct event_base *base = event_base_new(); //创建一个event,设置为等待fd可读,记录超时,回调函数以及参数 ev = event_new(base, fd, EV_TIMEOUT|EV_READ, cb_func,(char *)"read event"); //将event挂在event_base中 event_add(ev, &five_seconds); event_base_dispatch(base); } int main(int argc, char* argv[]) { //键盘输入作为监听文件描述服 main_loop(0); return 0; }我们的程序是通过event_base_dispatch函数进入事件循环的,类似的进入循环的函数还有event_base_loop,后者相当于增强版dispatch,可以设置具体的循环方式。
#define EVLOOP_ONCE 0x01 #define EVLOOP_NONBLOCK 0x02 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 int event_base_loop(struct event_base *base, int flags);
libevent还提供了event_base_loopexit和event_base_loopbreak函数用以跳出事件循环。
查看libevent的官方首页,发现除了memcache是基于libevent库做的开发外,大名鼎鼎的chromium浏览器的Unix和mac版本也是基于libevent事件驱动库的。以后写一些底层的服务可以考虑使用它,毕竟自己维护一个异步非阻塞IO的框架还是异常麻烦的。
我在网上搜集了一个基于libevent的http库实现的简单的“web服务器”代码,在vc2010下配置运行成功,下载地址http://dl.dbank.com/c079f7ytkk