libevent
头文件:
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
编译时需要加 -levent
event2接口
旧版libevent中,一般只能操作一个全局的event_base,而在新版libevent中,event_base交由用户来管理,用户可以创建删除event_base,也可以把event注册到不同的event_base上。
新建一个 event_base
#include <event2/event.h>
struct event_base *event_base_new(void);
释放一个event_base
#include <event2/event.h>
void event_base_free(struct event_base *eb);
event的生命周期
event的生命周期与相关的函数关系密切
用户自己创建的event是uninitialized
的,需要使用event_assign()
进行初始化,或者直接使用event_new()从无到有创建一个新的初始化了的event。在初始化时,完成了回调函数的绑定。
event的初始状态是non-pending,表示这个event不会被触发。
新建(并初始化)一个 event
struct event *event_new(struct event_base *base, evutil_socket_t fd, short events,
event_callback_fn callback, void *callback_arg);
新建event需要给定event_base, evutil_socket_t与系统相兼容,在linux下实际就是int,与socket()返回的类型一致
#ifdef WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif
events是一组flag,用于表示要监视的事件类型,还会影响event的一些行为,包括:
- EV_TIMEOUT - 监视超时的事件 需要说明的是,在调用event_new()时,这个flag是不用设置的,如果event发生超时,则必然会触发,无论设置与否
- EV_READ - 监视可读的事件
- EV_WRITE - 监视可写的事件
- EV_SIGNAL - 监视信号量
- EV_PERSIST - 永久生效,否则触发一次后就失效了
- EV_ET - 设置边缘触发(edge-triggered) callback和callback_arg是回调操作所需的,不再详述 新建的event是non-pending状态的
初始化一个event
int event_assign(struct event *ev,
struct event_base *base, evutil_socket_t fd, short events,
event_callback_fn callback, void *callback_arg);
这个不会申请内存,其他同event_new()
释放一个event
void event_free(struct event *ev);
判断event是否初始化/被释放
int event_initialized(const struct event *ev);
将event置为pending状态
int event_add(struct event *ev, const struct timeval *timeout);
其中timeout可以指定超时时间,超时和EV_TIMEOUT配合使用。如果timeout如果为NULL,则表示永不超时,struct timeval的结构为:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
额外说句,操作当前时间对应的timeval可以用
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
将event置为non-pending状态
int event_del(struct event *ev);
检查event是否为pending状态
int event_pending(const struct event *ev, short events, struct timeval *tv);
需要注意的是,不需要查询event是否为active状态,因为在active时,线程正在执行回调函数,其他函数需要等到回调执行完毕,而此时已经退出了active状态
将event置为active状态
void event_active(struct event *ev, int res, short/* deprecated */);
res是要手动指派的flag
bufferevent
简介
数据缓冲区是libevent为IO缓冲区操作提供的一种通用机制,bufferevent 由一个底层的传输端口(如套接字)。
数据缓冲区是由读缓冲区和写缓冲区两部分组成。
创建和释放
使用函数bufferevent_socket_new()
,创建bufferevent的套接字.
struct bufferevent * bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options
);
// 参数options:
// BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口
// 成功时返回bufferevent,失败则返回NULL
例:
struct event_base *base;
struct bufferevent *bev;
base = event_base_new();
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
释放函数:
void bufferevent_free(struct bufferevent *bev);
设置回调函数
回调在创建的时候也可以设置。
void bufferevent_setcb(
struct bufferevent *bufev,
bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用
bufferevent_event_cb eventcb,
void *cbarg
;
操作数据
- 向bufferevent的输出缓冲区添加数据
int bufferevent_write( struct bufferevent *bufev, const void *data, size_t size );
- 从bufferevent的输入缓冲区移除数据
size_t bufferevent_read( struct bufferevent *bufev, void *data, size_t size );
evconnlistener_new_bind
简介
evconnlistener其实是对event_base和event的封装而已。
链接监听器
使用函数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
);
参数含义:
- *base:base参数
- cb:listener回调函数
- ptr:自定义参数
- flags:标志值(参见附录,LEV_OPT_*)
- backlog:
- *sa:服务端的sockaddr
- socklen:上一个参数的长度
释放时使用
vconnlistener_free()
定义:void evconnlistener_free(struct evconnlistener *lev);
附录
bufferevent 定义
struct bufferevent {
struct event_base *ev_base;
//读事件
struct event ev_read;
//写事件
struct event ev_write;
//读缓冲区,输入缓冲
struct evbuffer *input;
//写缓冲区,输出缓冲
struct evbuffer *output;
//读水位
struct event_watermark wm_read;
//写水位
struct event_watermark wm_write;
//发生读触发用户设置的回调
evbuffercb readcb;
//发生写触发用户设置的回调
evbuffercb writecb;
//发生错误触发用户设置的回调
everrorcb errorcb;
//当前设置的回调函数传递的参数,和上面3个回调配合使用
void *cbarg;
//设置读超时时间,默认为0
int timeout_read; /* in seconds */
//设置写超时时间,默认为0
int timeout_write; /* in seconds */
//当前事件是否可用
short enabled; /* events that are currently enabled */
};
//水位
struct event_watermark {
//低水位
size_t low;
//高水位
size_t high;
};
bufferevent_cb 回调参数
参数|含义 –|– EV_EVENT_READING|读取操作时发生某事件,具体是哪种事件请看其他标志 BEV_EVENT_WRITING|写入操作时发生某事件,具体是哪种事件请看其他标志 BEV_EVENT_ERROR|操作时发生错误 BEV_EVENT_TIMEOUT|发生超时 BEV_EVENT_EOF|遇到文件结束指示 BEV_EVENT_CONNECTED|请求的连接过程已经完成实现客户端的时候可以判断
evconnlistener定义
//一系列的工作函数,因为listener可以用于不同的协议。
struct evconnlistener_ops {
int (*enable)(struct evconnlistener *);
int (*disable)(struct evconnlistener *);
void (*destroy)(struct evconnlistener *);
void (*shutdown)(struct evconnlistener *);
evutil_socket_t (*getfd)(struct evconnlistener *);
struct event_base *(*getbase)(struct evconnlistener *);
};
//一层一层封装,加上隔离
struct evconnlistener {
const struct evconnlistener_ops *ops; //操作函数
void *lock; //锁变量,用于线程安全
evconnlistener_cb cb; //用户的回调函数
evconnlistener_errorcb errorcb; //发生错误时的回调函数
void *user_data; //回调函数的参数,当回调函数执行时候,通过形参传入回调函数内部
unsigned flags; //属性标志 ,例如socket套接字属性,可以是阻塞,非阻塞,reuse等。
short refcnt; //引用计数
unsigned enabled : 1; //位域为1.即只需一个比特位来存储这个成员
};
struct evconnlistener_event {
struct evconnlistener base;
struct event listener; //内部event,插入到event_base,完成监听
};
listener 标志值
参数 | 含义 |
---|---|
LEV_OPT_LEAVE_SOCKETS_BLOCKING | 默认情况下,当连接监听器接收到新的客户端socket连接后,会把该socket设置为非阻塞的。如果设置该选项,那么就把客户端socket保留为阻塞的 |
LEV_OPT_CLOSE_ON_FREE | 当连接监听器释放时,会自动关闭底层的socket |
LEV_OPT_CLOSE_ON_EXEC | 为底层的socket设置close-on-exec标志 |
LEV_OPT_REUSEABLE: | 在某些平台,默认情况下当一个监听socket被关闭时,其他socket不能马上绑定到同一个端口,要等一会儿才行。设置该标志后,Libevent会把该socket设置成reuseable。这样,关闭该socket后,其他socket就能马上使用同一个端口 |
LEV_OPT_THREADSAFE | 为连接监听器分配锁。这样可以确保线程安全 |
参考
libevent中的bufferevent原理
详解libevent网络库(一)—框架的搭建
详解libevent网络库(二)—即时聊天通讯
libevent绑定、监听和读写数据
使用bufferevent进行libevent服务端和客户端的开发
Libevent源码分析—–连接监听器evconnlistener
socket编程与libevent2的一些归纳总结