Libevent速成总结
makefile和cmake
一、概述
1.反应堆模型
在epool事件结构体中我们将event.data.ptr 指向一个带有函数指针的结构体,每当事件触发,就通过event.data.ptr调用对应的函数。这种类似于信号的代码模式称为反应堆(reactor)模型。Libevent正是通过reactor模型写出的一套库。
struct epoll_event {
__uint32_t events; 宏定义读和写EPOLLIN读EPOLLOUT,边缘触发EPOLLET
epoll_data_t data; 联合体
};
联合体如下:
typedef union epoll_data {
void *ptr;//此指针可以指向一个带有函数的结构体,当有对应的事件发生时,可以将次指针指向的结构体内的函数手动进行调用。这就是反应堆模型。
int fd; //一般通过此fd判断是否是对应的listenfd或connfd进行判断是否有新的client链接和哪个旧的client在发送数据
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
2.事件状态转换图
未决和非未决:
非未决: 没有资格被处理
未决: 有资格被处理,但尚未被处理
event_new --> event ---> 非未决 --> event_add --> 未决 --> dispatch() && 监听事件被触发 --> 激活态
--> 执行回调函数 --> 处理态 --> 非未决 event_add && EV_PERSIST --> 未决 --> event_del --> 非未决
一、普通事件和信号
1.libevent的常用函数
创建event_base
struct event_base *event_base_new(void);
void event_base_free(struct event_base *base);
int event_reinit(struct event_base *base);//在调用fork创建新的进程后,需要重新调用此函数,否则可能event_base会失效。
循环 event_base中的事件
int event_base_dispatch(struct event_base *base);一直运行,直到没有已经注册的事件了,或者调用 了 event_base_loopbreak()或者 event_base_loopexit()为止。
int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
释放event_base
void event_base_free(struct event_base *base);
创建事件(将事件、event_base与fd绑定)事件变成非未决态
struct event *event_new(struct event_base *base, evutil_socket_t fd,short what, event_callback_fn cb, void *arg);
short what表示什么事件触发,可选择一个或多个:EV_READ、EV_READ、EV_PERSIST、EV_ET、signum等。
创建信号事件(将事件、event_base与sig绑定)事件变成非未决态
#define evsignal_new(base, signum, cb, arg) event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
添加事件和信号到event_base上 事件变成未决态
#define evsignal_add(ev, tv) event_add((ev),(tv))
将事件和信号从event_base中删除,事件和信号变成非激活和非未决态。
#define evsignal_del(ev) event_del(ev)
2.demo代码
/*
* This sample code shows how to use Libevent to read from a named pipe.
* XXX This code could make better use of the Libevent interfaces.
*
* XXX This does not work on Windows; ignore everything inside the _WIN32 block.
*
* On UNIX, compile with:
* cc -I/usr/local/include -o event-read-fifo event-read-fifo.c \
* -L/usr/local/lib -levent
*/
#include <event2/event-config.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
static void
fifo_read(evutil_socket_t fd, short event, void *arg)
{
char buf[255];
int len;
struct event *ev = arg;
#ifdef _WIN32
DWORD dwBytesRead;
#endif
fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
(int)fd, event, arg);
#ifdef _WIN32
len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL);
/* Check for end of file. */
if (len && dwBytesRead == 0) {
fprintf(stderr, "End Of File");
event_del(ev);
return;
}
buf[dwBytesRead] = '\0';
#else
len = read(fd, buf, sizeof(buf) - 1);
if (len <= 0) {
if (len == -1)
perror("read");
else if (len == 0)
fprintf(stderr, "Connection closed\n");
event_del(ev);
event_base_loopbreak(event_get_base(ev));
return;
}
buf[len] = '\0';
#endif
fprintf(stdout, "Read: %s\n", buf);
}
/* On Unix, cleanup event.fifo if SIGINT is received. */
#ifndef _WIN32
static void
signal_cb(evutil_socket_t fd, short event, void *arg)
{
struct event_base *base = arg;
event_base_loopbreak(base);
}
#endif
int
main(int argc, char **argv)
{
struct event *evfifo;
struct event_base* base;
#ifdef _WIN32
HANDLE socket;
/* Open a file. */
socket = CreateFileA("test.txt", /* open File */
GENERIC_READ, /* open for reading */
0, /* do not share */
NULL, /* no security */
OPEN_EXISTING, /* existing file only */
FILE_ATTRIBUTE_NORMAL, /* normal file */
NULL); /* no attr. template */
if (socket == INVALID_HANDLE_VALUE)
return 1;
#else
struct event *signal_int;
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);
}
socket = open(fifo, O_RDONLY | O_NONBLOCK, 0);
if (socket == -1) {
perror("open");
exit(1);
}
fprintf(stderr, "Write data to %s\n", fifo);
#endif
/* Initalize the event library */
base = event_base_new();
/* Initalize one event */
#ifdef _WIN32
evfifo = event_new(base, (evutil_socket_t)socket, EV_READ|EV_PERSIST, fifo_read,
event_self_cbarg());
#else
/* catch SIGINT so that event.fifo can be cleaned up */
signal_int = evsignal_new(base, SIGINT, signal_cb, base);
event_add(signal_int, NULL);
evfifo = event_new(base, socket, EV_READ|EV_PERSIST, fifo_read,
event_self_cbarg());
#endif
/* Add it to the active events, without a timeout */
event_add(evfifo, NULL);
event_base_dispatch(base);
event_base_free(base);
#ifdef _WIN32
CloseHandle(socket);
#else
close(socket);
unlink(fifo);
#endif
libevent_global_shutdown();
return (0);
}
三、bufferevent和evconnlistener
1.常用函数
创建、销毁bufferevent:
将bufferevent和fd绑定在一起并add到event_base中,相当于调用event_new和event_add
bufferevent创建的时候写缓冲是可用的,读缓冲是不可以用的。可以通过buffereventset设置缓冲区的读写限制
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);
fd: 封装到bufferevent内的 fd
options:BEV_OPT_CLOSE_ON_FREE
void bufferevent_socket_free(struct bufferevent *ev);
给bufferevent设置回调
void bufferevent_setcb(struct bufferevent * bufev,
bufferevent_data_cb readcb,
bufferevent_data_cb writecb,
bufferevent_event_cb eventcb,
void *cbarg );
readcb: 设置 bufferevent 读缓冲,对应回调 read_cb{ bufferevent_read() 读数据 }
writecb: 设置 bufferevent 写缓冲,对应回调 write_cb { } -- 给调用者,发送写成功通知。 可以 NULL
eventcb: 设置 事件回调。 也可传NULL
cbarg: 上述回调函数使用的 参数。
void bufferevent_enable(struct bufferevent *bufev, short events);
events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
默认、write 缓冲是 enable、read 缓冲是 disable
bufferevent读写函数
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
bufferevent_read(bev, MESSAGE, strlen(MESSAGE));
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
将evconnlistener与服务器ip和端口绑定并add到event_base上
struct evconnlistener * evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog , const struct sockaddr *sa, int socklen); void evconnlistener_free(struct evconnlistener *lev);
cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成, 数据读写操作,进行通信。
ptr: 回调函数的参数
flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
LEV_OPT_CLOSE_ON_FREE :设置了这个选项,释放连接监听器会关闭底层套接字。
LEV_OPT_REUSEABLE:端口复用
backlog: listen() 2参。 -1 表最大值
sa:服务器自己的地址结构体
socklen:服务器自己的地址结构体大小。
释放监听服务器:
void evconnlistener_free(struct evconnlistener *lev);
2.代码demo
/*
This example program provides a trivial server program that listens for TCP
connections on port 9995. When they arrive, it writes a short message to
each client connection, and closes each connection once it is flushed.
Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
static const char MESSAGE[] = "Hello, World!\n";
static const int PORT = 9995;
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;
struct sockaddr_in sin;
#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
event_base_dispatch(base);
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);
printf("done\n");
return 0;
}
static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ);
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
printf("flushed answer\n");
bufferevent_free(bev);
}
}
static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts */
bufferevent_free(bev);
}
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);
}