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 voidsignal_cb(evutil_socket_t fd, short event, void *arg){struct event *signal = arg;printf("got signal.\n"); // 如果用户取消两次,这时删除改事件if (called >= 2)event_del(signal);called++;}intmain(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 */ // 打开fifosocket = 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. */intsetnonblock(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. */voidbuffered_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. */voidbuffered_on_write(struct bufferevent *bev, void *arg){}/** * Called by libevent when there is an error on the underlying socket * descriptor. */voidbuffered_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. */voidon_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));}intmain(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;}