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;
}