Libevent是一个轻量级的开源的高性能的网络库,被众多的开源项目使用,例如大名鼎鼎的memcached等。具有如下的显著的特点:事件驱动,轻量级(和ACE相比的话),跨平台,支持多路的IO复用技术,支持定时器、信号等事件。
Libevent提供了事件通知,io缓存事件,定时器,超时,异步解析dns,事件驱动的http server以及一个rpc框架。
事件通知:当文件描述符可读可写时将执行回调函数。
Io缓存:缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作io。
定时器:libevent提供了定时器的机制,能够在一定的时间间隔之后调用回调函数。
异步的dns解析:libevent提供了异步解析dns服务器的dns解析函数集。
事件驱动的http服务器:libevent提供了一个简单的,可集成到应用程序中的HTTP服务器。
RPC客户端服务器框架:libevent为创建RPC服务器和客户端创建了一个RPC框架,能自动的封装和解封数据结构。
libevent是一个典型的reactor模式的实现。这里需要说明一个什么是reactor模式。普通的函数调用机制如下:程序调用某个函数,函数执行,程序等待,函数将结果返回给调用程序(如果含有函数返回值的话)。Reactor模式的基本流程如下:应用程序需要提供相应的接口并且注册到reactor上,如果相应的事件发生的话,那么reactor将自动调用相应的注册的接口函数(类似于.net中的回调函数)。
Libevent安装比较简单,安装过程如下(ubuntu下,其他系统下类似,libevent的版本2.0.12-stable ):
xuqiang@ubuntu:~/libevent/libevent-2.0.12-stable$ ./configure
xuqiang@ubuntu:~/libevent/libevent-2.0.12-stable$ make
xuqiang@ubuntu:~/libevent/libevent-2.0.12-stable$ sudo make install
定时器:
/* * XXX This sample code was once meant to show how to use the basic Libevent * interfaces, but it never worked on non-Unix platforms, and some of the * interfaces have changed since it was first written. It should probably * be removed or replaced with something better. * * Compile with: * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent */ #include <sys/types.h> #include <event2/event-config.h> #include <sys/stat.h> #include <time.h> #include <sys/time.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <event2/event.h> #include <event2/event_struct.h> #include <event2/util.h> struct timeval lasttime; int event_is_persistent; static void timeout_cb(evutil_socket_t fd, short event, void *arg) { struct timeval newtime, difference; /* 这里是如何使用参数的 */ struct event *timeout = arg; double elapsed; evutil_gettimeofday(&newtime, NULL); evutil_timersub(&newtime, &lasttime, &difference); elapsed = difference.tv_sec + (difference.tv_usec / 1.0e6); printf("timeout_cb called at %d: %.3f seconds elapsed.\n", (int)newtime.tv_sec, elapsed); lasttime = newtime; if (! event_is_persistent) { struct timeval tv; evutil_timerclear(&tv); tv.tv_sec = 2; event_add(timeout, &tv); } } int main(int argc, char **argv) { struct event timeout; struct timeval tv; struct event_base *base; int flags; if (argc == 2 && !strcmp(argv[1], "-p")) { event_is_persistent = 1; flags = EV_PERSIST; } else { event_is_persistent = 0; flags = 0; } /* Initalize the event library初始化程序库 */ base = event_base_new(); /* Initalize one event,初始化一个event,注意在这里如何传递参数的 */ event_assign(&timeout, base, -1, flags, timeout_cb, (void*) &timeout); evutil_timerclear(&tv); tv.tv_sec = 2; /* 添加一个event */ event_add(&timeout, &tv); evutil_gettimeofday(&lasttime, NULL); /* 开始运行 */ event_base_dispatch(base); return (0); }信号量:
/* * Compile with: * cc -I/usr/local/include -o signal-test \ * signal-test.c -L/usr/local/lib -levent 1. libevent头文件的使用? 简单的应用中仅仅需要包含头文件<event.h>即可, 在头文件<event.h>中仅仅是包含了另外的头文件 2. 何时调用函数event_base_free? 如果event_base不在使用的情况下,调用event_base_free 删除event_base对象 */ #include <sys/types.h> #include <sys/stat.h> #include <sys/queue.h> #include <unistd.h> #include <sys/time.h> #include <signal.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> // #include <event.h> int called = 0; static void signal_cb(evutil_socket_t fd, short event, void *arg) { struct event *signal = arg; printf("got signal.\n"); // 如果用户取消两次,这时删除改事件 if (called >= 2) event_del(signal); called++; } int main(int argc, char **argv) { struct event signal_int; struct event_base* base; /* Initalize the event library */ base = event_base_new(); /* Initalize one event */ event_assign(&signal_int, base, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int); event_add(&signal_int, NULL); event_base_dispatch(base); /* 如果一个event_base对象不再使用的话,可以调用 改函数删除event_base,但是这里需要注意的时event_base 并不删除和event_base相关联的event对象,也不删除 打开的其他资源 */ event_base_free(base); return (0); }
#include <sys/types.h> #include <sys/stat.h> #include <sys/queue.h> #include <unistd.h> #include <sys/time.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <event.h> static void fifo_read(int fd, short event, void *arg) { char buf[255]; int len; struct event *ev = arg; /* Reschedule this event这里需要重新添加改事件 */ event_add(ev, NULL); fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n", fd, event, arg); len = read(fd, buf, sizeof(buf) - 1); if (len == -1) { perror("read"); return; } else if (len == 0) { fprintf(stderr, "Connection closed\n"); return; } buf[len] = '\0'; fprintf(stdout, "Read: %s\n", buf); } int main(int argc, char **argv) { struct event evfifo; struct stat st; const char *fifo = "event.fifo"; int socket; if (lstat(fifo, &st) == 0) { if ((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); exit(1); } } unlink(fifo); if (mkfifo(fifo, 0600) == -1) { perror("mkfifo"); exit(1); } /* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */ // 打开fifo socket = open(fifo, O_RDWR | O_NONBLOCK, 0); if (socket == -1) { perror("open"); exit(1); } fprintf(stderr, "Write data to %s\n", fifo); /* Initalize the event library初始化程序库 */ event_init(); /* Initalize one event */ /* 初始化event类型,注册回调函数fifo_read */ event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); /* Add it to the active events, without a timeout 添加event */ event_add(&evfifo, NULL); /* 开始调度“event” */ event_dispatch(); return (0); }
/* * libevent echo server example using buffered events. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* Required by event.h. */ #include <sys/time.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <err.h> /* Libevent. */ #include <event.h> /* Port to listen on. */ #define SERVER_PORT 5555 /** * A struct for client specific data, also includes pointer to create * a list of clients. */ struct client { /* The clients socket. */ int fd; /* The bufferedevent for this client. */ struct bufferevent *buf_ev; }; /** * Set a socket to non-blocking mode. */ int setnonblock(int fd) { int flags; flags = fcntl(fd, F_GETFL); if (flags < 0) return flags; flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) return -1; return 0; } /** * Called by libevent when there is data to read. */ void buffered_on_read(struct bufferevent *bev, void *arg) { /* Write back the read buffer. It is important to note that * bufferevent_write_buffer will drain the incoming data so it * is effectively gone after we call it. */ bufferevent_write_buffer(bev, bev->input); } /** * Called by libevent when the write buffer reaches 0. We only * provide this because libevent expects it, but we don't use it. */ void buffered_on_write(struct bufferevent *bev, void *arg) { } /** * Called by libevent when there is an error on the underlying socket * descriptor. */ void buffered_on_error(struct bufferevent *bev, short what, void *arg) { struct client *client = (struct client *)arg; if (what & EVBUFFER_EOF) { /* Client disconnected, remove the read event and the * free the client structure. */ printf("Client disconnected.\n"); } else { warn("Client socket error, disconnecting.\n"); } bufferevent_free(client->buf_ev); close(client->fd); free(client); } /** * This function will be called by libevent when there is a connection * ready to be accepted. */ void on_accept(int fd, short ev, void *arg) { int client_fd; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); struct client *client; client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { warn("accept failed"); return; } /* Set the client socket to non-blocking mode. */ if (setnonblock(client_fd) < 0) warn("failed to set client socket non-blocking"); /* We've accepted a new client, create a client object. */ client = calloc(1, sizeof(*client)); if (client == NULL) err(1, "malloc failed"); client->fd = client_fd; /* Create the buffered event. * * The first argument is the file descriptor that will trigger * the events, in this case the clients socket. * * The second argument is the callback that will be called * when data has been read from the socket and is available to * the application. * * The third argument is a callback to a function that will be * called when the write buffer has reached a low watermark. * That usually means that when the write buffer is 0 length, * this callback will be called. It must be defined, but you * don't actually have to do anything in this callback. * * The fourth argument is a callback that will be called when * there is a socket error. This is where you will detect * that the client disconnected or other socket errors. * * The fifth and final argument is to store an argument in * that will be passed to the callbacks. We store the client * object here. */ client->buf_ev = bufferevent_new(client_fd, buffered_on_read, buffered_on_write, buffered_on_error, client); /* We have to enable it before our callbacks will be * called. */ bufferevent_enable(client->buf_ev, EV_READ); printf("Accepted connection from %s\n", inet_ntoa(client_addr.sin_addr)); } int main(int argc, char **argv) { int listen_fd; struct sockaddr_in listen_addr; struct event ev_accept; int reuseaddr_on; /* Initialize libevent. */ event_init(); /* Create our listening socket. */ listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) err(1, "listen failed"); memset(&listen_addr, 0, sizeof(listen_addr)); listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = INADDR_ANY; listen_addr.sin_port = htons(SERVER_PORT); if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0) err(1, "bind failed"); if (listen(listen_fd, 5) < 0) err(1, "listen failed"); reuseaddr_on = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)); /* Set the socket to non-blocking, this is essential in event * based programming with libevent. */ if (setnonblock(listen_fd) < 0) err(1, "failed to set server socket to non-blocking"); /* We now have a listening socket, we create a read event to * be notified when a client connects. */ event_set(&ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL); event_add(&ev_accept, NULL); /* Start the event loop. */ event_dispatch(); return 0; }