http://blog.csdn.net/xuqianghit/article/details/6640920
libevnet学习笔记1
1. Libevent是什么?
Libevent是一个轻量级的开源的高性能的网络库,被众多的开源项目使用,例如大名鼎鼎的memcached等。具有如下的显著的特点:事件驱动,轻量级(和ACE相比的话),跨平台,支持多路的IO复用技术,支持定时器、信号等事件。
2. Libevent功能
Libevent提供了事件通知,io缓存事件,定时器,超时,异步解析dns,事件驱动的http server以及一个rpc框架。
事件通知:当文件描述符可读可写时将执行回调函数。
Io缓存:缓存事件提供了输入输出缓存,能自动的读入和写入,用户不必直接操作io。
定时器:libevent提供了定时器的机制,能够在一定的时间间隔之后调用回调函数。
异步的dns解析:libevent提供了异步解析dns服务器的dns解析函数集。
事件驱动的http服务器:libevent提供了一个简单的,可集成到应用程序中的HTTP服务器。
RPC客户端服务器框架:libevent为创建RPC服务器和客户端创建了一个RPC框架,能自动的封装和解封数据结构。
3. Reactor模式
libevent是一个典型的reactor模式的实现。这里需要说明一个什么是reactor模式。普通的函数调用机制如下:程序调用某个函数,函数执行,程序等待,函数将结果返回给调用程序(如果含有函数返回值的话)。Reactor模式的基本流程如下:应用程序需要提供相应的接口并且注册到reactor上,如果相应的事件发生的话,那么reactor将自动调用相应的注册的接口函数(类似于.net中的回调函数)。
4. Libevent安装
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
5. 几个简单的示例程序
定时器:
- /*
- * 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);
- }
io事件:
- #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);
- }
bufferevent使用:
- /*
- * 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;