Libevent的使用
ps:该笔记大部分内容来自刘丹冰Aceld · 语雀 (yuque.com),侵权删
2.event_base
每个 event_base 结构 体持有一个事件集合,可以检测以确定哪个事件是激活的.
①创建event_base
//分配并且返回一个新的具有默认设置的 event_base
struct event_base *event_base_new(void);
//创建复杂的event_base
struct event_base *event_base_new_with_config(const struct event_config *cfg);
struct event_config *event_config_new(void);
void event_config_free(struct event_config *cfg);
一个事件 struct event (io事件,定时器事件,信号事件)。
②释放event_base
使用event_base之后,需要手动释放
void event_base_free(struct event_base *base);
③设置优先级
必须在任何事件激活之前调用这个函数。
int event_base_priority_init(struct event_base *base, int n_priorities);
④event_base与fork
fork后需要重新初始化event_base
int event_reinit(struct event_base *base);
⑤event_loop
event_base_loop()函数运行 event_base 直到其中没有已经注册的事件为止。
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
int event_base_loop(struct event_base *base, int flags);
//不带flag的event_base_loop
int event_base_dispatch(struct event_base *base);
⑥停止event_loop
event_base_loopexit()
让 event_base 在给定时间之后停止循环。如果 tv 参数为 NULL, event_base 会立即停止循环,没有延时。如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出。
event_base_loopbreak ()
让 event_base 立即退出循环。它与 event_base_loopexit (base,NULL)
的不同在于,如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出。
int event_base_loopexit(struct event_base *base, const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);
//判断事件循环是如何退出的
int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);
⑦转储event_base的状态C++
调用 event_base_dump_events()可以将event_base事件状态列表输出到指定的文件中。
void event_base_dump_events(struct event_base *base, FILE *f);
3.event
①创建事件
所有新创建的事件都处于已初始化和非未决状态 ,需要调用 event_add()可以使其成为未决的。
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
struct event *event_new(struct event_base *base, evutil_socket_t fd,
short what, event_callback_fn cb,
void *arg);
//释放事件
void event_free(struct event *event);
②事件标志
EV_TIMEOUT//这个标志表示某超时时间流逝后事件成为激活的。
EV_READ //表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的。
EV_WRITE //表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的。
EV_SIGNAL //用于实现信号检测
EV_PERSIST //表示事件是“持久的,对其调用 event_del ()使事件成为非未决的。
EV_ET //表示如果底层的 event_base 后端支持边沿触发事件,则事件应该是边沿触发的
③信号事件
//创建信号事件
#define evsignal_new(base, signum, cb, arg) \
event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
//添加信号事件,使其成为未决
#define evsignal_add(ev, tv) \
event_add((ev),(tv))
//删除信号事件,使其成为非未决
#define evsignal_del(ev) \
event_del(ev)
//确定事件的状态
#define evsignal_pending(ev, what, tv_out) \
event_pending((ev), (what), (tv_out))
④事件的未决和非未决
构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。需要使用event_add()将事件添加到 event_base。
//设置未决事件
int event_add(struct event *ev, const struct timeval *tv);
//设置非未决事件
int event_del(struct event *ev);
⑤设置事件的优先级
在初始化事件之后, 但是在添加到 event_base 之前,可以为其设置优先级。
事件的优先级是一个在 0和 event_base 的优先级减去1之间的数值。
如果不为事件设置优先级,则默认的优先级将会是 event_base 的优先级数目除以2。
int event_priority_set(struct event *event, int priority);
⑥检查事件状态
event_pending()函数确定给定的事件是否是未决的或者激活的。
int event_pending(const struct event *ev, short what, struct timeval *tv_out);
//event_get_fd()和 event_get_signal()返回为事件配置的文件描述符或者信号值。
#define event_get_signal(ev) /* ... */
evutil_socket_t event_get_fd(const struct event *ev);
//event_get_base()返回为事件配置的
struct event_base *event_get_base(const struct event *ev);
//event_base。event_get_events()返回事件的标志(EV_READ、EV_WRITE 等)。
short event_get_events(const struct event *ev);
//event_get_callback()和 event_get_callback_arg() 返回事件的回调函数及其参数指针。
event_callback_fn event_get_callback(const struct event *ev);
void *event_get_callback_arg(const struct event *ev);
//返回事件的优先级
int event_get_priority(const struct event *ev);
//复制所有为事件分配的字段到提供的指针中
void event_get_assignment(const struct event *event,
struct event_base **base_out,
evutil_socket_t *fd_out,
short *events_out,
event_callback_fn *callback_out,
void **arg_out);
⑦只触发一次事件
如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 event_base_once()。
int event_base_once(struct event_base *, evutil_socket_t, short,
void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);
⑧手动激活事件
需要在事件的条件没有触发的时候让事件成为激活的。
void event_active(struct event *ev, int what, short ncalls);
4.Bufferevent
每个 bufferevent 都有一个输入缓冲区和一个输出缓冲区 ,它们的类型都是“struct evbuffer”。
①创建基于套接字的bufferevent
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);
//bufferevent_options有如下选项
● BEV_OPT_CLOSE_ON_FREE :释放 bufferevent 时关闭底层传输端口。这将关闭底层套接字,释放底层 bufferevent 等。
● BEV_OPT_THREADSAFE :自动为 bufferevent 分配锁,这样就可以安全地在多个线程中使用 bufferevent。
● BEV_OPT_DEFER_CALLBACKS :设置这个标志时, bufferevent 延迟所有回调。
● BEV_OPT_UNLOCK_CALLBACKS :默认情况下,如果设置 bufferevent 为线程安全 的,则 bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent 在执行回调的时候不进行锁定。
②在bufferevent上启动链接
int bufferevent_socket_connect(struct bufferevent *bev,
struct sockaddr *address, int addrlen);
③释放bufferevent操作
bufferevent 内部具有引用计数,所以,如果释放 时还有未决的延迟回调,则在回调完成之前 bufferevent 不会被删除。
如果设置了 BEV_OPT_CLOSE_ON_FREE 标志,并且 bufferevent 有一个套接字或者底层 bufferevent 作为其传输端口,则释放 bufferevent 将关闭这个传输端口。
void bufferevent_free(struct bufferevent *bev);
④bufferevent回调
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
typedef void (*bufferevent_event_cb)(struct bufferevent *bev,
short events, void *ctx);
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
void bufferevent_getcb(struct bufferevent *bufev,
bufferevent_data_cb *readcb_ptr,
bufferevent_data_cb *writecb_ptr,
bufferevent_event_cb *eventcb_ptr,
void **cbarg_ptr);
//可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。
void bufferevent_enable(struct bufferevent *bufev, short events);
void bufferevent_disable(struct bufferevent *bufev, short events);
//可以调用 bufferevent_get_enabled()确定 bufferevent 上当前启用的事件。
short bufferevent_get_enabled(struct bufferevent *bufev);
⑤操作bufferevent中的数据
通过bufferevent得到evbuffer.
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev);
⑥向bufferevent的输出缓冲区添加数据
添加到缓冲区的数据会自动发送到对端
//从 data 处开 始的 size 字节数据添加到输出缓冲区的末尾
int bufferevent_write(struct bufferevent *bufev,
const void *data, size_t size);
//移除 buf 的所有内 容,将其放置到输出缓冲区的末尾
int bufferevent_write_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
⑦从bufferevent的输入缓冲区读取移除数据
//移除size大小的数据保存到data处,data需要保证足够的容量
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
//抽空输入缓冲区的所有内容,将其放置到 buf 中
int bufferevent_read_buffer(struct bufferevent *bufev,
struct evbuffer *buf);
⑧bufferevent的清空操作
失败时 bufferevent_flush()返回-1,如果没有数据被清空则返回 0,有数据被清空则返回 1。
int bufferevent_flush(struct bufferevent *bufev, short iotype, enum bufferevent_flush_mode state);
5.evBuffer
①创建和释放evbuffer
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *buf);
②.evbuffer与线程安全
//使能锁
int evbuffer_enable_locking(struct evbuffer *buf, void *lock);
void evbuffer_lock(struct evbuffer *buf);
void evbuffer_unlock(struct evbuffer *buf);
③.检查evbuffer
size_t evbuffer_get_length(const struct evbuffer *buf);
//这个函数返回 evbuffer 存储的字节数,它在2.0.1-alpha 版本中引入。
④向evbuffer添加数据
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
//这个函数添加 data 处的 datalen 字节到 buf 的末尾,
//成功时返回0,失败时返回-1。
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);
//这些函数添加格式化的数据到 buf 末尾。
//格式参数和其他参数的处理分别与 C 库函数 printf 和 vprintf 相同。函数返回添加的字节数。
int evbuffer_expand(struct evbuffer *buf, size_t datlen);
//这个函数修改缓冲区的最后一块,或者添加一个新的块,
//使得缓冲区足以容纳 datlen 字节, 而不需要更多的内存分配。
⑤.evbuffer数据移动
//evbuffer_add_buffer()将 src 中的所有数据移动到 dst 末尾,成功时返回0,失败时返回-1。
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);
//evbuffer_remove_buffer()函数从 src 中移动 datlen 字节到 dst 末尾,尽量少进行复制。如果字节数小于 datlen,所有字节被移动。函数返回移动的字节数。
int evbuffer_remove_buffer(struct evbuffer *src,
struct evbuffer *dst,
size_t datlen);
6.evconnlistener连接监听器
①.创建和释放evconnlistener
新连接到达时,监听 器调用给出的回调函数
可识别的标志:
- LEV_OPT_LEAVE_SOCKETS_BLOCKING 会设置为阻塞的
- LEV_OPT_CLOSE_ON_FREE 放连接监听器会关闭底层套接字
- LEV_OPT_CLOSE_ON_EXEC 底层套接字设置 close-on-exec 标志
- LEV_OPT_REUSEABLE 套接字是可重用的
- LEV_OPT_THREADSAFE 为监听器分配锁,这样就可以在多个线程中安全地使用
//假定已经将套接字绑定到要监听的端口,然后通过 fd 传入这个套接字。
struct evconnlistener *
evconnlistener_new(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
evutil_socket_t fd);
//分配和绑定套接字,可以调用 evconnlistener_new_bind() ,
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);
②.链接监听器回调
调整 evconnlistener 的回调函数,使用evconnlistener_set_cb。
typedef void (*evconnlistener_cb)(struct evconnlistener *listener,
evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);
void evconnlistener_set_cb(struct evconnlistener *lev,
evconnlistener_cb cb, void *arg);
③.启用和禁用 evconnlistener
int evconnlistener_disable(struct evconnlistener *lev);
int evconnlistener_enable(struct evconnlistener *lev);
④.检测 evconnlistener
evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev);
struct event_base *evconnlistener_get_base(struct evconnlistener *lev);
⑤.侦测错误
typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr);
void evconnlistener_set_error_cb(struct evconnlistener *lev,
evconnlistener_errorcb errorcb);
7.other
①.日志消息回调设置
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG 1
#define EVENT_LOG_WARN 2
#define EVENT_LOG_ERR 3
/* Deprecated; see note at the end of this section */
#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG
#define _EVENT_LOG_MSG EVENT_LOG_MSG
#define _EVENT_LOG_WARN EVENT_LOG_WARN
#define _EVENT_LOG_ERR EVENT_LOG_ERR
typedef void (*event_log_cb)(int severity, const char *msg);
void event_set_log_callback(event_log_cb cb);
②.致命错误回调设置
typedef void (*event_fatal_cb)(int err);
void event_set_fatal_callback(event_fatal_cb cb);
③. 内存管理回调设置
void event_set_mem_functions(void *(*malloc_fn)(size_t sz),
void *(*realloc_fn)(void *ptr, size_t sz),
void (*free_fn)(void *ptr));
④锁和线程的设置
#ifdef WIN32
int evthread_use_windows_threads(void);
#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
#endif
#ifdef _EVENT_HAVE_PTHREADS
int evthread_use_pthreads(void);
#define EVTHREAD_USE_PTHREADS_IMPLEMENTED
#endif
example
#include <stdio.h>
#include <stdlib.h>
#include <event.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <event2/listener.h>
//读事件回调
void read_cb(struct bufferevent*bev,void *arg)
{
int fd = *(int*)arg;
char buf[128] = 0;
int ret = bufferevent_read(bev, buf, sizeof(buf));
if(ret < 0)
{
printf("read error\n");
}
else
{
printf("recv from %d:%s",fd,buf);
}
}
//其他事件回调
void event_cb(struct bufferevent*bev,short what,void *arg)
{
if(what &BEV_EVNET_EOF)
{
printf("客户端下线\n");
bufferevent_free(bev); //释放buffereven对象
}
else
{
printf("未知错误");
}
}
void listener_cb(struct evconnlistener*listener, evutil_socket_t fd,struct sockaddr*addr,int socklen, void *arg)
{
printf("接受%d的连接\n",fd);
struct event_base *base = arg; //需要从主函数传递过来
//针对已经存在的socket创建bufferevent对象
//参数如下:返回buffer对象
struct bufferevent*bev = bufferevent_socket_new(base, //事件集合
fd, //文件描述符
BEV_OPT_CLOSE_ON_FREE //释放bufferevent对象则断开连接
);
//给bufferevnet设置回调函数
bufferevent_setcb(bev, //buffereven对象
read_cb, //读回调
NULL, //写回调
event_cb, //其他回调
(void*)fd; //参数
//使能事件类型
bufferevent_enable(bev, //buffereven对象
EV_READ //使能读
)
}
int main()
{
//创建一个事件集合
struct event_base *base = event_base_new();
if(NULL==base)
{
exit(1);
}
//创建监听对象
struct sockaddr_in server_addr;
memset(&server_addr, 0 ,sizeof(server_addr));
server_addr.sin_port = 8000;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//一步完成:创建socket 绑定socket,监听,接受链接四部
//参数如下:
struct evconnlistener *listener = evconnlistener_new_bind(base, //事件集合
listener_cb, //当有链接时调用的函数
NULL, //回调函数的参数
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
10, //监听队列的长度
(struct sockaddr*)&server_addr, //tcp绑定信息
sizeof(server_addr) //tcp信息的大小
);
if(NULL == listener)
{
exit(1);
}
//监听集合中的事件
event_base_dispatch(base);
//释放两个对象
evconnlistener_free(listener); //连接对象
event_base_free(base); //释放集合
}