Libevent共享库
一、Libevent 概述
Libevent 是一个高效的、可移植的异步 I/O 的共享库,它追求:移植性、速度、扩展性、便捷性。Libevent 分为以下几个组件:
|
evutil |
通用函数,对不同平台网络实现的抽象 |
|
event、event_base |
Libevent 的核心,提供了针对不同平台特性的抽象API。告诉你socket是否准备好了读写,以及检测系统信号 |
|
bufferevent |
提供了对 event 的的进一步的更方便的封装, 带缓冲区,告诉你什么时候套接字已经产生了 I/O |
|
evbuffer |
实现了 bufferevents 所需的缓冲区,并提供了函数进行高效的访问 |
|
evhttp |
一个简单的 HTTP 客户端/服务器 的实现 |
|
evdns |
一个简单的 DNS 客户端/服务器 的实现 |
|
evrpc |
一个简单的 RPC 的实现 |
按照默认创建 Libevent,将会安装以下共享库:
|
libevent_core |
event_base 、evbuffer、bufferevent以及其他工具函数 |
|
libevent_extra |
提供了一些无关紧要的紧要的函数,一些协议相关的实现,包括:HTTP、DNS、RPC |
|
libevent |
由于历史原因而存在,包括l ibevent_core 和 libevent_extra 两部分,不应该使用libevent (将来会去掉) |
|
libevent_pthreads |
在 pthreads 可移植线程库的基础上添加了线程同步相关的实现 |
|
libevent_openssl |
这个库提供基于 bufferevents 和 OpenSSL 共享库的加密通信 |
头文件:
所有头文件都安装在 event2 目录下 (/usr/include/event2)。分为三大类:
|
API headers |
定义了访问Libevent的公开接口 |
|
Compatibility headers |
包括对过时函数的定义,不应该导入这些头文件,除非从旧版本的 Libevent 导入程序 |
|
结构体 headers |
定义了一些结构体,一般头文件名有后缀 “__struct.h” |
二、设置Libevent库
记录日志(event2/event.h)
|
C #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);
/* libevent 可以将内部错误和警告记录下来。默认将信息打印到标准错误, 使用 event_set_log_callback() 函数可以设置自定义的日志记录函数。 当参数 cb 为 NULL 时,将设置为默认。 */ |
处理致命错误(event2/event.h)
|
C typedef void (*event_fatal_cb)(int err); void event_set_fatal_callback(event_fatal_cb cb);
/* 当 libevent 检测到不可恢复的致命错误时,默认行为是调用 exit() 或者 abort() 函数来 终止当前进程。使用 event_set_fatal_callback() 函数可以自定义处理函数。 注意:一旦执行了自定义函数,说明出现了 bug,可能是 libevent,也可能是自己的代码, 所以,在自定义函数中,不能将控制流还给 libevent 。 */ |
内存管理(event2/event.h)
|
C void event_set_mem_functions(void *(*malloc_fn)(size_t sz), void *(*realloc_fn)(void *ptr, size_t sz), void (*free_fn)(void *ptr));
/* 默认使用 C 库中的内存管理函数,使用 event_set_mem_function() 函数可以自定义 内存管理函数。
注意: 1. 替换内存管理函数影响接下来所有的 libevent 发出的分配(allocate)、重置(resize)、 释放(free)调用,所有,必须确保在调用其他 Libevent 函数之前替换回。否则,Libevent 将使用自定义的函数归还由 C 库的 malloc() 函数分配的内存。(???) 2. 自定义的分配和释放函数,必须像 C 库一样保证内存对齐。 3. 4. 5. 6. 7. 如果多线程使用 Libevent,必须保证线程安全。 8. */。 |
锁和线程(event2/event.h)
|
C++ #ifdef WIN32int evthread_use_windows_threads(void); #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED #endif #ifdef _EVENT_HAVE_PTHREADSint evthread_use_pthreads(void); #define EVTHREAD_USE_PTHREADS_IMPLEMENTED #endif
/* Libevent 中的结构体有三种: 只能单线程使用的:多个线程同时使用不安全; 可选带锁:可以告诉每个结构体对象是否需要多线程同时使用; 总是带锁:总是支持多线程
必须告诉 Libevent 使用哪个锁函数。如果使用 Windows 线程或者 pthread 的话, 则不用告诉,已经默认设置好了。 */ |
如果使用不同的线程库,则要求该线程库要实现以下功能:
- Locks
- locking
- unlocking
- lock allocation
- lock destruction
- Conditions
- condition variable creation
- condition variable destruction
- waiting on a condition variable
- signaling/broadcasting to a condition variable
- Threads
- thread ID detection
然后,才能调用以下接口告诉 Libevent 相应的函数是什么:
|
C #define EVTHREAD_WRITE 0x04 #define EVTHREAD_READ 0x08 #define EVTHREAD_TRY 0x10
#define EVTHREAD_LOCKTYPE_RECURSIVE 1 #define EVTHREAD_LOCKTYPE_READWRITE 2
#define EVTHREAD_LOCK_API_VERSION 1
/* 可识别的锁类型(locktype)有三种: 0 : 标准、非递归锁 EVTHREAD_LOCKTYPE_RECURSIVE : 递归锁,拥有锁的线程可以再次拥有,其他线程必须等待拥有锁的线程彻底释放才可拥有 EVTHREAD_LOCKTYPE_READWRITE : 读者之间不排斥,写者排斥所有读者,写者排斥一切 */ struct evthread_lock_callbacks { int lock_api_version; // 必须设置成 EVTHREAD_LOCK_API_VERSION unsigned supported_locktypes; // 必须设置成 EVTHREAD_LOCKTYPE_* void *(*alloc)(unsigned locktype); // 创建一个 locktype 类型的新锁 void (*free)(void *lock, unsigned locktype); // 释放锁持有的所有资源
/* 可识别的 mode 有: EVTHREAD_READ : 只能针对 READWRITE 锁,对读者申请或者释放锁 EVTHREAD_WRITE : 只能针对 READWRITE 锁,对写者申请或者释放锁 EVTHREAD_TRY : 只针对 locking,如果可以立刻获得则获得锁 */ // 尝试获取锁,成功返回 0,失败返回非 0 。 int (*lock)(unsigned mode, void *lock);
// 释放锁,成功返回 0,失败返回非 0 。 int (*unlock)(unsigned mode, void *lock); };
int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);
/* id_fn : 返回一个 unsigned long 类型的数字,这个数字要能够区分是哪个线程在 调用函数 */ void evthread_set_id_callback(unsigned long (*id_fn)(void));
struct evthread_condition_callbacks { int condition_api_version; // 必须为 EVTHREAD_CONDITION_API_VERSION
// 返回指向一个竞性变量的指针 void *(*alloc_condition)(unsigned condtype);
// 释放竞性变量的占用内存及其所拥有资源 void (*free_condition)(void *cond);
// 如果 broadcast 为假,唤醒一个等待竞性变量的线程, // 如果 broadcast 为真,唤醒所有等待竞性变量的线程 int (*signal_condition)(void *cond, int broadcast);
/* cond : 由 alloc_condition 分配; lock : 由 evthread_lock_callbacks 中的 alloc 函数分配 timeout : 可选
等待,直到竞性变量被 signalled 或者超时;
返回值: -1 : 错误 0 : 竞性变量被 signalled 1 : 超时 */ int (*wait_condition)(void *cond, void *lock, const struct timeval *timeout); }; int evthread_set_condition_callbacks( const struct evthread_condition_callbacks *); |
调试锁的使用
|
C void evthread_enable_lock_debugging(void); #define evthread_enable_lock_debuging() evthread_enable_lock_debugging()
/* 使用该接口,可以捕捉锁的错误,包括: 释放自身不持有的锁; 非递归锁 re-locking
为了安全起见,应该在锁变量创建之前、设置线程函数之前,调用该接口 */ |
调试 event
|
C /* Libevent 可以检测并报告在使用 event 时的一些错误,包括: 使用未初始化的 struct event 对象; 对 pending 状态的 event 再次初始化 等等 */
// 在创建 event_base 之前调用 void event_enable_debug_mode(void);
// 为了避免调试时内存用尽,应该使用这个接口 // 在非调试模式下,这个接口不会产生影响 void event_debug_unassign(struct event *ev); |
检测 Libevent 的版本号
|
C++ #define LIBEVENT_VERSION_NUMBER 0x02000300 #define LIBEVENT_VERSION "2.0.3-alpha"const char *event_get_version(void); ev_uint32_t event_get_version_number(void); |
释放全局 Libevent 结构体
|
C++ // 进程结束之前调用 void libevent_global_shutdown(void); |
三、event_base
在使用 Libevent 函数之前,需要创建若干个 struct event_base 对象。一个 struct event_base 管理一系列的事件(event),并且可以决定激活哪个事件。如果带锁的话,可以多线程访问一个 struct event_base 对象,但是,只能在其中一个线程中循环(loop)。
event_base 可以使用下列函数(backend)来决定哪个事件是否准备好:
▪ select
▪ poll
▪ epoll
▪ kqueue
▪ devpoll
▪ evport
▪ win32
设置默认的 event_base (event/event2.h)
|
C struct event_base *event_base_new(void); |
设置复杂的 event_base(event2/event.h)
|
C struct event_config *event_config_new(void); struct event_base *event_base_new_with_config(const struct event_config *cfg); void event_config_free(struct event_config *cfg);
/* 使用 event_base_new_with_config 可以对 event_base 进行配置 */ |
|
C // 告诉 Libevent 避免使用名字为 method 的函数作为 backend int event_config_avoid_method(struct event_config *cfg, const char *method);
enum event_method_feature { EV_FEATURE_ET = 0x01, // 支持边沿触发 EV_FEATURE_O1 = 0x02, // 添加、删除、激活事件的时间复杂度为 O(1) EV_FEATURE_FDS = 0x04, // 支持任意多的文件描述符或者套接字 };
// 告诉 Libevent 不要用任何不包含某特征的函数作为 backend int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
enum event_base_config_flag { // 不为 event_base 分配锁 EVENT_BASE_FLAG_NOLOCK = 0x01,
// 选择 backend 函数时忽略 EVENT_* 环境变量 EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
// 只在 Windows 使用,启动时即启用所有必要的 IOCP 分发逻辑,而不是按需启用 EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,evebnt
// 不在调用 timeout 回调函数之前(timeout用尽)检查时间, // 在调用 timeout callback 之后检查 EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
// 如果使用 epoll 作为 backend,使用更快的基于 changelist 的后端。 // 使用 epoll-changelist backend,当同一个文件描述符在 dispatch 函数之间调用时 // 发生多次状态变化,可以避免不需要的系统调用,但是对于使用 dup() 函数克隆的文件 // 描述符,将触发一个内核 bug。 EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
// 默认情况下,Libevent 尝试使用平台提供的最快的时钟机制。使用这个标志,告诉Libevent // 使用精度更高的时钟机制 EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 };
// 告诉 libevent 在构建 event_base 时设置标志 int event_config_set_flag(struct event_config *cfg, enum event_base_config_flag flag); |
|
C // 目前只对 Winedows 平台的 IOCP 有用 // 告诉 event_config,它创建的 event_base 要尽可能的利用指定的 CPU int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus);
// 为了防止优先级倒挂,限制检查高优先级之前允许低优先级事件callback的调用次数 // 如果 max_interval 非空,在每次调用 callback 后检查时间,如果超过了 max_retval // 就重新检查(re-scan)高优先级事件。 // 如果 max_callback 非负,在 max_callbacks 次 callback 调用后检查更多事件 int event_config_set_max_dispatch_interval(struct event_config *cfg, const struct timeval *max_interval, int max_callbacks, int min_priority); |
获取 event_base 使用的后端(backend)函数
|
C const char **event_get_supported_methods(void);
const char *event_base_get_method(const struct event_base *base); enum event_method_feature event_base_get_features(const struct event_base *base); |
释放 event_base
|
C void event_base_free(struct event_base *base); |
设置优先级(event2/event.h)
|
C // 设置 event 优先级级别数, 默认只有一个优先级,从 0 ~ n_priorities-1 int event_base_priority_init(struct event_base *base, int n_priorities);
// 获取支持的优先级级别 int event_base_get_npriorities(struct event_base *base); |
fork() 重新初始化(event2/event.h)
|
C // 在子进程中继续使用 event_base,需要重新初始化
int event_reinit(struct event_base *base); |
四、启动事件循环
启动循环(event2/event.h)
|
C // 一旦注册了事件,即可让 Libevent 开始等待事件
#define EVLOOP_ONCE 0x01 // 只等待一次事件出现 #define EVLOOP_NONBLOCK 0x02 // 不阻塞 #define EVLOOP_NO_EXIT_ON_EMPTY 0x04 // 无 pending 无 active 时,循环也不结束
// 如果设置了 EVLOOP_NO_EXIT_ON_EMPTY,只能调用 event_base_loopbreak() // 和 event_base_loopexit() 结束 int event_base_loop(struct event_base *base, int flags);
/* 描述如下: while (any events are registered with the loop, or EVLOOP_NO_EXIT_ON_EMPTY was set) {
if (EVLOOP_NONBLOCK was set, or any events are already active) If any registered events have triggered, mark them active. else Wait until at least one event has triggered, and mark it active.
for (p = 0; p < n_priorities; ++p) { if (any event with priority of p is active) { Run all active events with priority of p. break; // Do not run any events of a less important priority } }
if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set) break; }
*/
// 无 flags 参数,直到没有注册的事件或者调用 event_base_loopbreak() // 或者调用 event_base_loopexit() int event_base_dispatch(struct event_base *base); |
停止循环(event2/event.h)
|
C int event_base_loopexit(struct event_base *base, const struct timeval *tv); int event_base_loopbreak(struct event_base *base);
// 是否调用过 event_base_loopexit() int event_base_got_exit(struct event_base *base); // 是否调用过 event_base_loopbreak() int event_base_got_break(struct event_base *base); |
继续循环
|
C // 在 callback 中调用
int event_base_loopcontinue(struct event_base *); |
时间
|
C // 在 callback 中调用,避免系统调用 // 在 callback 中,获取缓冲时间(调用 callback 时存入,不准确), // 否则调用 evutil_gettimeofday() 来获取真实时间 int event_base_gettimeofday_cached(struct event_base *base, struct timeval *tv_out);
// 更新实时时间 int event_base_update_cache_time(struct event_base *base); |
Dump 下 event_base 的状态
|
C void event_base_dump_events(struct event_base *base, FILE *f); |
遍历事件
|
C typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *);
// arg 作为 event_base_foreach_event_cb 的第三个参数 int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg); |
五、关于事件
事件是 Libevent 的基本操作单元,每一个事件都是一种条件的表示,包括:
▪ 准备好读或者准备好写的文件描述符
▪ 变成准备好读或者变成准备好写的文件描述符
▪ 时间用尽
▪ 信号产生
▪ 一个用户触发的事件
事件的生命周期类似,如下图所示:
构建 event 对象(event2/event.h)
|
C #define EV_TIMEOUT 0x01 // 超过 timeout 之后,事件将 active #define EV_READ 0x02 // 文件描述符可读时,事件 active #define EV_WRITE 0x04 // 文件描述符可写时,事件 active #define EV_SIGNAL 0x08 // 事件检测事件 #define EV_PERSIST 0x10 // persistent 事件 #define EV_ET 0x20 // 边沿触发,和 EV_READ 或者 EV_WRITE 才有意义
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
/* 在堆上创建一个 struct event * 对象 what : EV_* 或运算 传给 event_callback_fn 的第三个参数 */ 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);
/* 如果要使用 event 对象自身作为 callback 的参数,此时还不存在具体的 event 对象, 为了解决这一问题,可以使用 event_self_cbarg() 函数。 返回一个 magic 指针,作为 event_new() 函数的第三个指针,即可。 */ void *event_self_cbarg(); |
|
C /* 对于纯超时事件,定义了一些宏来代替 event_*() 接口 */
#define evtimer_new(base, callback, arg) \ event_new((base), -1, 0, (callback), (arg)) #define evtimer_add(ev, tv) \ event_add((ev),(tv)) #define evtimer_del(ev) \ event_del(ev) #define evtimer_pending(ev, tv_out) \ event_pending((ev), EV_TIMEOUT, (tv_out)) |
|
C /* 信号检测事件 */
#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)) |
|
C /* 对一个 event 对象初始化。 如果要将 struct event 结构体嵌入一个更大的结构体使用,使用 event_new() 函数 会破坏与其他 Libevent 版本之间的二进制兼容性。要使用 event_assign() 来创建具体 的 struct event 对象。 注意:尽量直接使用 event_new(),除非清楚的知道,在堆上分配内存会导致极大的性能下降。 在将来的 Libevent 版本中,event_assgin() 可能会引起难以诊断的错误。 不能对一个处于 pending 状态的 event 对象初始化。 */
int event_assign(struct event *event, struct event_base *base, evutil_socket_t fd, short what, void (*callback)(evutil_socket_t, short, void *), void *arg);
// 实例如下:对堆上的对象初始化 // 这个 event_assign() 也可以对栈上的对象初始化 struct event_pair { evutil_socket_t fd; struct event read_event; struct event write_event; }; void readcb(evutil_socket_t, short, void *); void writecb(evutil_socket_t, short, void *); struct event_pair *event_pair_new(struct event_base *base, evutil_socket_t fd) { struct event_pair *p = malloc(sizeof(struct event_pair)); if (!p) return NULL; p->fd = fd; event_assign(&p->read_event, base, fd, EV_READ|EV_PERSIST, readcb, p); event_assign(&p->write_event, base, fd, EV_WRITE|EV_PERSIST, writecb, p); return p; } |
|
C #define evtimer_assign(event, base, callback, arg) \ event_assign(event, base, -1, 0, callback, arg) #define evsignal_assign(event, base, signum, callback, arg) \ event_assign(event, base, signum, EV_SIGNAL|EV_PERSIST, callback, arg) |
|
C // 获取 struct event 结构体的大小,返回字节数目。
size_t event_get_struct_event_size(void); |
使 events 变成 pending 或者 non-pending
|
C /* 使 event 变成 pending 状态,如果 tv 不为空,则超时后,状态变为 pending。 如果 event 的状态已经是 pending,则先变为 unpending,在 tv 之后,再次变为 pending; 如果 tv 为 NULL,则无效。 */ int event_add(struct event *ev, const struct timeval *tv);
int event_del(struct event *ev);
/* 移除 event 的超时组件,不改变其 I/O 组件和信号组件,如果只有时钟组件, 则效果相当于 event_del。 */ int event_remove_timer(struct event *ev); |
带优先级的事件
|
C /* 给事件设置优先级,这个函数应该在 event 初始化之后,event_add 之前调用。 */ int event_priority_set(struct event *event, int priority); |
观察 event 的状态
|
C /* 看事件是 pending 还是 active, The event_pending function determines whether the given event is pending or active. If it is, and any of the flags EV_READ, EV_WRITE, EV_SIGNAL, and EV_TIMEOUT are set in the what argument, the function returns all of the flags that the event is currently pending or active on. If tv_out is provided, and EV_TIMEOUT is set in what, and the event is currently pending or active on a timeout, then tv_out is set to hold the time when the event’s timeout will expire. */ int event_pending(const struct event *ev, short what, struct timeval *tv_out);
#define event_get_signal(ev) /* ... */ evutil_socket_t event_get_fd(const struct event *ev); struct event_base *event_get_base(const struct event *ev); short event_get_events(const struct event *ev); 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); |
正在运行的事件
|
C // 不允许在另外一个线程中调用 struct event *event_base_get_running_event(struct event_base *base); |
设置非持久事件
|
C // 创建一个一次非持久事件。作用类似于 event_new(),使用默认优先级,当 callback 执行完 // 之后,Libevent 释放内部结构体。 int event_base_once(struct event_base *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *, const struct timeval *); |
手动激活事件
|
C /* 激活事件: ev : 要激活的事件 what : EV_READ,EV_WRITE 和 EV_TIMEOUT 的组合 注意:不要陷入无限递归调用。 */
void event_active(struct event *ev, int what, short ncalls); |
Optimizing common timeouts
|
C /* 大量事件拥有相同的超时时间时,用这个函数,可以提升性能 */
const struct timeval *event_base_init_common_timeout ( struct event_base *base, const struct timeval *duration); |
从清理后的内存中分离出初始化后的事件
|
C int event_initialized(const struct event *ev);
#define evsignal_initialized(ev) event_initialized(ev) #define evtimer_initialized(ev) event_initialized(ev) |
六、用于Libevent的帮助函数和数据类型
基本类型
|
C #ifdef WIN32 #define evutil_socket_t intptr_t #else #define evutil_socket_t int #endif |
|
Type |
Width |
Signed |
Maximum |
Minimum |
|
ev_uint64_t |
64 |
No |
EV_UINT64_MAX |
0 |
|
ev_int64_t |
64 |
Yes |
EV_INT64_MAX |
EV_INT64_MIN |
|
ev_uint32_t |
32 |
No |
EV_UINT32_MAX |
0 |
|
ev_int32_t |
32 |
Yes |
EV_INT32_MAX |
EV_INT32_MIN |
|
ev_uint16_t |
16 |
No |
EV_UINT16_MAX |
0 |
|
ev_int16_t |
16 |
Yes |
EV_INT16_MAX |
EV_INT16_MIN |
|
ev_uint8_t |
8 |
No |
EV_UINT8_MAX |
0 |
|
ev_int8_t |
8 |
Yes |
EV_INT8_MAX |
EV_INT8_MIN |
时钟函数
|
C #define evutil_timeradd(tvp, uvp, vvp) /* ... */ #define evutil_timersub(tvp, uvp, vvp) /* ... */
#define evutil_timerclear(tvp) /* ... */ #define evutil_timerisset(tvp) /* ... */
#define evutil_timercmp(tvp, uvp, cmp)
int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); |
Socket API
|
C int evutil_closesocket(evutil_socket_t s);
#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
#define EVUTIL_SOCKET_ERROR() #define EVUTIL_SET_SOCKET_ERROR(errcode) #define evutil_socket_geterror(sock) #define evutil_socket_error_to_string(errcode)
int evutil_make_socket_nonblocking(evutil_socket_t sock);
int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
int evutil_make_socket_closeonexec(evutil_socket_t sock);
int evutil_socketpair(int family, int type, int protocol, evutil_socket_t sv[2]); |
字符串处理函数
|
C ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);
int evutil_snprintf(char *buf, size_t buflen, const char *format, ...); int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap); |
不依赖本地语言的字符串比较函数
|
C int evutil_ascii_strcasecmp(const char *str1, const char *str2); int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n); |
IPv6帮助和可移植函数
|
C const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); int evutil_inet_pton(int af, const char *src, void *dst);
int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen);
/* 比较两个地址 */ int evutil_sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2, int include_port); |
结构体宏可移植函数
|
C #define evutil_offsetof(type, field) /* ... */ |
安全的随机数生成器
|
C void evutil_secure_rng_get_bytes(void *buf, size_t n);
int evutil_secure_rng_init(void); void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); |
七、Bufferevents概念和基础
一个 “bufferevent” 由三部分组成,一个底层传输(比如套接字)、一个读缓冲区和一个写缓冲区。与 event 不同,当读到或者写出足够多的数据,bufferevent 才会调用提供的回调函数。有多种类型的 bufferevent:
- 基于套接字的 bufferevents
○ 使用一个流式套接字传输数据,使用 event_* 接口作为 backend。
- 异步 I/O bufferevents
○ A bufferevent that uses the Windows IOCP interface to send and receive data to an underlying stream socket. (Windows only; experimental.)
- 过滤器 bufferevents
○ 能对数据做处理
- 成对 bufferevents
○ 两个 bufferents 互相传输数据
注意:
Bufrerevents 目前只能支持像 TCP 这样的基于流的协议,在将来可能会支持像UDP这样基于数据报的协议。
Bufferevents 和 evbuffers
读缓冲区和写缓冲区的类型都是 "struct evbuffer"。
回调函数和水位
每个 bufferevent 都有两个数据相关的回调函数:一个读回调函数和一个写回调函数。默认情况下,任意数据从底层读到读缓冲区则调用读回调函数;写缓冲区的数据全部写完(empty)则调用写回调函数。每个 bufferevent 都有四个水位:
- 读低水位
○ 读缓冲区数据一旦达到读低水位,则调用读 callback。默认是 0。
- 读高水位
○ 读缓冲区数据一旦达到读高水位,bufferevent 则停止读取数据,直到数据低于该水位。默认是无限。
- 写低水位
○ 写缓冲区数据数据一旦低于写低水位,则调用写 callback。默认是0。
- 写高水位
○ bufferevent不直接使用,当一个 bufferevent 是另一个 bufferevent 的底层时,有特殊意义。
对于非数据事件,bufferevent 也可以对一些“错误”或者“事件”提供回调函数:
- BEV_EVENT_READING
○ 对 bufferevent 进行读操作期间发生事件
- BEV_EVENT_WRITING
○ 对 bufferevent 进行写操作期间发生事件
- BEV_EVENT_ERROR
○ 对 bufferevent 操作期间发生错误。要知道是什么错误,调用 EVUTIL_SOCKET_ERROR()。
- BEV_EVENT_TIMEOUT
○ 超时
- BEV_EVENT_EOF
○ We got an end-of-file indication on the bufferevent.
- BEV_EVENT_CONNECTED
○ 在bufferevent上完成了请求连接。
回调函数推迟
bufferevents 的选项 flag
创建 bufferevent 时可以使用 flag 来调整它的行为。可识别的标志有:
▪ BEV_OPT_CLOSE_ON_FREE
- 当 bufferevent 释放时,关闭底层,将会关闭底层 socket,释放底层 bufferevent,等。
▪ BEV_OPT_THREADSAFE
- 给 bufferevent 自动分配锁,所以是多线程安全的。
▪ BEV_OPT_DEFER_CALLBACKS
- 推迟所有的 callback 。
▪ BEV_OPT_UNLOCK_CALLBACKS
- 默认情况下,当 bufferevent 被设置为线程安全的,在调用任何一个回调函数时,bufferevent 的锁被占有。设置了这个标志,当调用回调函数时,释放bufferevent 的锁。
基于套接字的 bufferevent
|
C /* 创建一个基于套接字的 bufferevent */ struct bufferevent *bufferevent_socket_new ( struct event_base *base, evutil_socket_t fd, enum bufferevent_options options);
/* 如果 bufferevent 的套接字还没有建立连接,可以使用 bufferevent_socket_connect() 建立连接 */ int bufferevent_socket_connect(struct bufferevent *bev, struct sockaddr *address, int addrlen);
/* 按照主机名,建立连接 */ int bufferevent_socket_connect_hostname(struct bufferevent *bev, struct evdns_base *dns_base, int family, const char *hostname, int port); int bufferevent_socket_get_dns_error(struct bufferevent *bev); |
对 bufferevent 的通用操作
|
C // 尽可能释放 bufferevent。 // 如果有推迟的 callback,则执行完 callback 之后再释放; // 如果写缓冲区有数据,在释放之前不会 flush。 // 如果设置了 BEV_OPT_CLOSE_ON_FREE,如果 bufferevent 有底层 transport,则关闭。 void bufferevent_free(struct bufferevent *bev);
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);
/* 使用 cbarg 可以传递数据给 callback,以 NULL 为参数可以关闭 callback */ 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); short bufferevent_get_enabled(struct bufferevent *bufev);
/* 调整水位。 如果 events 是 EV_READ,则调整读水位 如果 events 是 EV_WRITE,则跳转写水位 如果 events 是 EV_READ | EV_WRITE,同时调整两个水位 */ void bufferevent_setwatermark(struct bufferevent *bufev, short events, size_t lowmark, size_t highmark);
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); void /* 往 bufferevent 中添加数据,从 data 中添加 size 个数据到写缓冲区 成功返回 0,发生错误返回 -1。 */ int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size); /* 将 buf 中的内容全部移入 eventbuffer 的写缓冲区 成功返回 0,发生错误返回 -1。 */ int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
// 从 bufferevent 移除数据 /* 将从 bufferevent 中的数据最多移动 size 到缓冲区 data 返回实际移除的字节数 */ size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); /* 将 bufferevent 的读缓冲区抽干,全部放入 buf 成功返回 0,发生错误返回 -1。 */ int bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf); |
读写超时
|
C /* 当超过 timeout 时间都没有发生任何数据的读或者写,则调用 callback 如果 timeout 为 NULL,则移除对时钟的设置 一旦发生超时调用 callback ,其相应的操作将被关闭,事件 callback 可以继续调用通过 设置 BEV_EVENT_TIMEOUT | BEV_EVENT_READING 或者 BEV_EVENT_TIMEOUT | BEV_EVENT_WRITING */ void bufferevent_set_timeouts(struct bufferevent *bufev, const struct timeval *timeout_read, const struct timeval *timeout_write); |
Bufferevent 的 flush 操作
|
C /* 告诉 bufferevent 强制尽可能多的读写数据,忽略其他限制。具体细节与 bufferevent 的类型有关。 iotype : EV_READ、EV_WRITE、EV_READ | EV_WRITE state : BEV_NORMAL、BEV_FLUSH、BEV_FINISHED */ int bufferevent_flush(struct bufferevent *bufev, short iotype, enum bufferevent_flush_mode state); |
只能用于特定类型的关于 bufferevent 的函数
|
C /* 调整 bufferevent 的优先级 只能用于基于套接字的 bufferevent */ int bufferevent_priority_set(struct bufferevent *bufev, int pri); int bufferevent_get_priority(struct bufferevent *bufev);
/* 对于基于文件描述符的事件,设置文件描述符。只有基于套接字的 bufferevent 支持 setfd()。 失败返回 -1,setfd() 成功返回 0。 */ int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd); evutil_socket_t bufferevent_getfd(struct bufferevent *bufev);
struct event_base *bufferevent_get_base(struct bufferevent *bev);
struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev); |
对 bufferevent 手动加锁或者手动解锁
|
C /* 可以通过以下两个函数手动加锁或者手动解锁 只对创建时使用了 BEV_OPT_THREADSAFE 标志的 bufferevent 有用 并且 Libevent 启用 了支持多线程。 可以递归锁。 */ void bufferevent_lock(struct bufferevent *bufev); void bufferevent_unlock(struct bufferevent *bufev); |
八、bufferevents 进阶
成对 bufferevents
|
C // 一个 bufferevent 写,另一个 bufferevent 接收。pair[0] 和 pair[1] 互相连接。 // BEV_OPT_CLOSE_ON_FREE 标志不起作用,BEV_OPT_DEFER_CALLBACK 总是起作用。 int bufferevent_pair_new(struct event_base *base, int options, struct bufferevent *pair[2]);
struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev) |
过滤 bufferevent
|
C enum bufferevent_filter_result { BEV_OK = 0, BEV_NEED_MORE = 1, BEV_ERROR = 2 }; typedef enum bufferevent_filter_result (*bufferevent_filter_cb) ( struct evbuffer *source, struct evbuffer *destination, ev_ssize_t dst_limit, enum bufferevent_flush_mode mode, void *ctx);
/* 创建一个过滤 bufferevent。 underlying 接收到的数据在放入 filter bufferevent 之前,都经过 input_filter 过滤。 向外传输的数据,在到达 underlying 之前经过 output_filter 过滤。 */ struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying, bufferevent_filter_cb input_filter, bufferevent_filter_cb output_filter, int options, void (*free_context)(void *), void *ctx);
/*
*/ |
限制单词 读/写 的最大数目
|
C++ int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size); int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);
ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev); ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev); |
Rate-limiting(速率限制)
Libevent 的速率限制是采用令牌桶算法来决定采用一次读写多少字节。详细见官方文档。
|
C #define EV_RATE_LIMIT_MAX EV_SSIZE_MAX struct ev_token_bucket_cfg; struct ev_token_bucket_cfg *ev_token_bucket_cfg_new( size_t read_rate, size_t read_burst, size_t write_rate, size_t write_burst, const struct timeval *tick_len); void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg); int bufferevent_set_rate_limit(struct bufferevent *bev, struct ev_token_bucket_cfg *cfg);
struct bufferevent_rate_limit_group; struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new( struct event_base *base, const struct ev_token_bucket_cfg *cfg); int bufferevent_rate_limit_group_set_cfg( struct bufferevent_rate_limit_group *group, const struct ev_token_bucket_cfg *cfg); void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *); int bufferevent_add_to_rate_limit_group(struct bufferevent *bev, struct bufferevent_rate_limit_group *g); int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev);
ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev); ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev); ev_ssize_t bufferevent_rate_limit_group_get_read_limit( struct bufferevent_rate_limit_group *); ev_ssize_t bufferevent_rate_limit_group_get_write_limit( struct bufferevent_rate_limit_group *);
ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev); ev_ssize_t bufferevent_get_max_to_write(struct bufferevent *bev);
void bufferevent_rate_limit_group_get_totals( struct bufferevent_rate_limit_group *grp, ev_uint64_t *total_read_out, ev_uint64_t *total_written_out); void bufferevent_rate_limit_group_reset_totals( struct bufferevent_rate_limit_group *grp);
int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr); int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr); int bufferevent_rate_limit_group_decrement_read( struct bufferevent_rate_limit_group *grp, ev_ssize_t decr); int bufferevent_rate_limit_group_decrement_write( struct bufferevent_rate_limit_group *grp, ev_ssize_t decr);
int bufferevent_rate_limit_group_set_min_share( struct bufferevent_rate_limit_group *group, size_t min_share); |
Bufferevents 和 SSL
|
C enum bufferevent_ssl_state { BUFFEREVENT_SSL_OPEN = 0, BUFFEREVENT_SSL_CONNECTING = 1, BUFFEREVENT_SSL_ACCEPTING = 2 };
struct bufferevent * bufferevent_openssl_filter_new(struct event_base *base, struct bufferevent *underlying, SSL *ssl, enum bufferevent_ssl_state state, int options);
struct bufferevent * bufferevent_openssl_socket_new(struct event_base *base, evutil_socket_t fd, SSL *ssl, enum bufferevent_ssl_state state, int options);
SSL *bufferevent_openssl_get_ssl(struct bufferevent *bev); unsigned long bufferevent_get_openssl_error(struct bufferevent *bev); int bufferevent_ssl_renegotiate(struct bufferevent *bev); int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev);void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, int allow_dirty_shutdown); |
九、evbuffers
Libevent 的 evbuffer 是一个字节的队列,从队头取数据、从队尾添加数据。
创建和释放
|
C struct evbuffer *evbuffer_new(void); void evbuffer_free(struct evbuffer *buf); |
Evbuffer 线程安全
|
C // 默认情况下,evbuffer 不是线程安全的。使用 evbuffer_enable_blocking() // 函数可以达到线程安全的目的。 // 如果 lock 为 NULL,则使用 evthread_set_lock_creation_callback() 函数 // 提供的函数分配一个新锁。如果不为 NULL,使用 lock 参数作为锁。 int evbuffer_enable_locking(struct evbuffer *buf, void *lock); void evbuffer_lock(struct evbuffer *buf); void evbuffer_unlock(struct evbuffer *buf); |
观察(Inspecting) evbuffer
|
C // 返回 evbuffer 中的字节数 size_t evbuffer_get_length(const struct evbuffer *buf); // 返回 evbuffer(队列) 头部连续的字节数 size_t evbuffer_get_contiguous_space(const struct evbuffer *buf); |
添加数据到 evbuffer 的基础操作
|
C // 连接(append)datlen 字节的数据到 evbuffer。成功返回0,失败返回-1。 int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
// 添加(append)格式化的数据到 evbuffer int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...); int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);
// 调整 evbuffer 的空间,以能容下 datlen 的数据。 int evbuffer_expand(struct evbuffer *buf, size_t datlen); |
在 evbuffer 之间移动数据
|
C // 将 src 的所有数据移动到 dst 的尾部。 int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);
// 从 src 恰好移动 datlen 的数据到 dst,尽可能的少拷贝数据。如果 src 中数据 // 少于 datlen,就全部移动。 int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, size_t datlen); |
在 evbuffer 头部添加数据
|
C // 注意:永远不要在共享 bufferevent 的 evbuffer 之间使用。 int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src); |
对 evbuffer 内部布局重布局
|
C /* 将前 size 个字节“线性化”,通过拷贝或者移动将前 size 个字节放入一个内存块。 如果 size 是负数,则“线性化”整个 evbuffer; 如果 size 大于 evbuffer 中字节数,则返回 NULL; 否则,返回指向 evbuffer 中第一个字节的指针。 */ unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size); |
从 evbuffer 中移除数据
|
C // 移除 len 个字节 int evbuffer_drain(struct evbuffer *buf, size_t len);
// 移除 datlen 个字节,并将其拷贝到 data int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); |
从 evbuffer 中拷贝数据
|
C // 只拷贝,不移除数据; // 返回拷贝的字节数,-1 表示失败。 ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen);
// 从 pos 处拷贝数据 ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen); |
面向行的输入
|
C enum evbuffer_eol_style { EVBUFFER_EOL_ANY, // EVBUFFER_EOL_CRLF, // "\r\n" 或者 "\n" EVBUFFER_EOL_CRLF_STRICT, // "\r\n" EVBUFFER_EOL_LF, // "\n" EVBUFFER_EOL_NUL // 0 或者 NULL };
// 从 evbuffer 头部提取一行,并将其放入新分配的一个新字符串空间,以 NULL 结尾,返回之。 // 如果 n_read_out 不为空,则将其设置为返回的字节数。 // 如果不是一整行,则返回 NULL。 char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, enum evbuffer_eol_style eol_style); |
在 evbuffer 内查找
|
C /* 使用这个结构体,可以在 evbuffer 上面进行迭代 */ struct evbuffer_ptr { ev_ssize_t pos; // 标识了 evbuffer 中的一个偏移。 struct { /* internal fields */ } _internal; };
/* 扫描 evbuffer,寻找长度为 len 的字符串 what,返回一个 evbuffer_ptr,查找不到 时返回 -1。 如果提供了 start 参数,则表示开始扫描的地方,否则,从字符串的开头开始扫描。 */ struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start);
/* 只扫描 start 与 end 之间 */ struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end);
/* 像 evbuffer_readln() 函数一样,检查行尾,但是不拷贝行,返回 evbuffer_ptr 指向 行尾字符。如果 eol_len_out 非空,将设置为 EOL 字符串的长度。 */ struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, struct evbuffer_ptr *start, size_t *eol_len_out, enum evbuffer_eol_style eol_style);
enum evbuffer_ptr_how { EVBUFFER_PTR_SET, EVBUFFER_PTR_ADD };
// 操作 evbuffer_ptr,如果 how 是 EVBUFFER_PTR_SET,则设置为 position, // 如果是 EVBUFFER_PTR_ADD,则向前移动 position。 // 成功返回 0,失败返回 -1。 int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos, size_t position, enum evbuffer_ptr_how how); |
无拷贝观察数据
|
C struct evbuffer_iovec { void *iov_base; size_t iov_len; };
/* 返回指向每一个 evbufer 中每一个内存块(iov_base)的指针和长度。 n_vec : vec_out 数组的长度 如果 len 小于 0,evbuffer_peek() 尝试填满提供的所有 evbuffer_iovec; 否则,要么直到把 vec_out 全用完,要么 peek 的长度达到 len。 如果 evbuffer_iovec 够用,则返回实际使用的 evbuffer_iovec 的个数; 否则,则返回所需的 evbuffer_iovec 的个数。 如果 start_at 为空,则从 evbuffer 的开头开始 peek,否则从 start_at 处开始。 */ int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, struct evbuffer_ptr *start_at, struct evbuffer_iovec *vec_out, int n_vec); |
直接往 evbuffer 中添加数据
|
C /* 有时不想将数据写到一个字符数组再通过 evbuffer_add() 拷贝到 evbuffer, 而是想往 evbuffer 中直接插入数据,可以使用以下函数。 与 evbuffer_peek() 函数类似,使用 evbuffer_iovec 直接指向 evbuffer 中的 内存块。
evbuffer_reserve_space() 函数获取指向每一个内存卡的指针, 并将空间扩大到能容纳 size 个字节。
n_vecs 至少为是 1,如果只提供一个 evbuffer_iovec,Libevent 将保证请求的空间在 一个区域(extent)里面,但是为了达到这一目标,将会重布局(rearrange)buffer或者 浪费一些内存。为了更好的性能,要至少提供 2 个 evbuffer_iovec。 返回使用到的 evbuffer_iovec 的个数。 */ int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, struct evbuffer_iovec *vec, int n_vecs);
// 调用 evbuffer_commit_space() 函数之后, evbuffer_iovec 中的数据才真正是 // evbuffer 的一部分。 // 成功返回 0,失败返回 -1。 int evbuffer_commit_space(struct evbuffer *buf, struct evbuffer_iovec *vec, int n_vecs); |
使用 evbuffers 进行网络 I/O
|
C // 相当于 evbuffer_write_atmost() 函数的 howmuch 参数设置为负数。 int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd);
/* 尽可能地写至多 howmuch 个字节到 fd。如果 howmuch 是负数,则将 buffer 中的 数据全部写出。 成功 : 返回写的字节数 失败 : -1 如果失败,最好检查错误码 */ int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, ev_ssize_t howmuch);
// 最多读取 howmuch 个数据到 buffer 的尾部。如果 howmuch 是负数,则尽可能多读取数据 // 成功 : 返回读到的字节数 // EOF : 0 // 错误 : -1 // 可能是非阻塞操作,要检查错误码 int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch); |
Evbuffers 的回调函数
|
C /* 想知道数据添加到 evbuffer 和从 evbuffer 中移除的时机,可以使用回调函数。 */
struct evbuffer_cb_info { size_t orig_size; size_t n_added; size_t n_deleted; }; typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg);
// 给一个 evbuffer 添加回调函数,返回一个可以引用这个回调函数的不透明指针。 // cbarg 是提供给回调函数的参数。 // 添加新的回调函数不会移除旧的回调函数。 struct evbuffer_cb_entry; struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg);
// 移除 callback int evbuffer_remove_cb_entry(struct evbuffer *buffer, struct evbuffer_cb_entry *ent); int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg);
// 将 EVBUFFEDR_CB_ENABLED 清除掉,即可关闭 callback。 #define EVBUFFER_CB_ENABLED 1 int evbuffer_cb_set_flags(struct evbuffer *buffer, struct evbuffer_cb_entry *cb, ev_uint32_t flags); int evbuffer_cb_clear_flags(struct evbuffer *buffer, struct evbuffer_cb_entry *cb, ev_uint32_t flags);
// calback 推迟调用。当 evbuffer 发生变化时,不立即调用 callback。 int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base); |
Evbuffer I/O 避免数据拷贝
|
C // Libevent 提供了方法避免过多的数据拷贝 // 这个函数将一段数据的引用加到 evbuffer 的尾部。这个过程没有数据拷贝, // evbuffer 只是保存了存放在 data 的长度为 datalen 的字符串指针。 // 因此,只要 evbuffer 仍在使用这个引用,这个指针就一直有效。 // 当 evbuffer 不再需要数据时,将调用提供的 cleanupfn 函数。 // 成功返回 0,失败返回 -1。 typedef void (*evbuffer_ref_cleanup_cb)(const void *data, size_t datalen, void *extra); int evbuffer_add_reference(struct evbuffer *outbuf, const void *data, size_t datlen, evbuffer_ref_cleanup_cb cleanupfn, void *extra); |
添加文件到一个 evbuffer
|
C // fd 不是套接字描述符,并且要求可读。 // 成功返回 0,失败返回 -1。 int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset, size_t length); |
细粒度文件块控制
|
C struct evbuffer_file_segment;
/* 创建一个 evbuffer_file_segment 对象,来描述文件 fd 中从偏移 offset 开始, 长度为 length 的数据。如果出现错误,将返回 NULL。
flag 可以设置为: EVBUF_FS_CLOSE_ON_FREE : 使用 evbuffer_file_segment_free() 释放 file segment 时,将关闭文件。 EVBUF_FS_DISABLE_MMAP : 不对这个文件使用内存映射相关的函数 EVBUF_FS_DISABLE_SENDFILE : 不对这个文件使用 sendfile 类似的函数 EVBUF_FS_DISABLE_LOCKING : 不对文件块分配锁,非线程安全 */ struct evbuffer_file_segment *evbuffer_file_segment_new ( int fd, ev_off_t offset, ev_off_t length, unsigned flags); void evbuffer_file_segment_free(struct evbuffer_file_segment *seg); int evbuffer_add_file_segment(struct evbuffer *buf, struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length); |
将一个 evbuffer 加入到另一个
|
C // 将 outbuf 的引用(而不是自身)添加到 inbuf,看起来就像数据拷贝。 // inbuf 中缓冲区序列的变化不会影响 outbuf。 // 不能递归引用:如果一个 evbuffer 是另一个的 outbuf,那么就不能是另一个的 inbuf。 int evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf); |
冻结数据
|
C // 临时禁止头部或者尾部的数据变化。 // 用于防止 outbuf 的头部或者 inbuf 的尾部发生意外修改。 int evbuffer_freeze(struct evbuffer *buf, int at_front); int evbuffer_unfreeze(struct evbuffer *buf, int at_front); |


浙公网安备 33010602011771号