[ evBuffer - 数据封装 ]
一.evBuffer介绍
BufferEvent在进行读取和写入数据时,将数据存放在evBuffer中,因此,要想获取数据的相关信息—>需要对evBuffer结构进行解析。
二.struct evbuffer* 变量的赋值2种方式
方式1:手动创建+手动向evbuffer中添加数据
struct evbuffer* evbuf = evbuffer_new(); //手动创建evbuffer
evbuffer_add(evbuf,"I am a student",100); //手动将字符串添加到evbuf中
evbuffer_add_printf(evbuf,"%s,%s,%d","Tom","Student",24); //手动将字符串格式化添加到evbuf中
... ...
evbuffer_free(evbuf); //手动释放evbuffer
方式2:bufferevent_get_input/output–>获取输入/输出evbuffer
struct evbuffer *input =bufferevent_get_input(bev); //获取输入evbuffer
struct evbuffer *output =bufferevent_get_output(bev); //获取输出evbuffer
注意:
- bufferevent_get_input/bufferevent_get_output只是获取输入/输出缓冲区中的数据,并没有将数据从evbuffer中移走。
- 而,bufferevent_write是真正的向输出缓冲区中写入数据;bufferevent_read是真正的读走输入缓冲区中的数据。
三.与evbuffer相关的函数API总结
创建 / 销毁evbuffer | |
---|---|
struct evbuffer *evbuffer_new(void); | 分配和返回一个新的空的evbuffer |
void evbuffer_free(struct evbuffer *buf); | 释放evbuffer和其内容 |
获取/读写evbuffer | |
---|---|
struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); | 获取eventbuffer的输入缓冲区evbuffer |
struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); | 获取eventbuffer的输出缓冲区evbuffer |
size_t evbuffer_get_length(const struct evbuffer *buf); | 返回evbuffer的字节数length |
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size); | 将data中的数据写size个字节存放在bufev中 |
int bufferevent_write_buffer(struct bufferevent *bufev,struct evbuffer *buf); | |
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); | 从bufev中读取size个字节的数据存放在data中 |
int bufferevent_read_buffer(struct bufferevent *bufev,struct evbuffer *buf); |
逐行读取:evbuffer_readln
/*
功能:从缓冲区buffer中读取一行数据,存储到返回值char*中
返回值:成功返回读取到的一行数据(返回的读取到的字符串不包括行结束符),失败返回NULL
参数:
n_read_out:被设置为读取到的一行数据的字节个数
eol_style:行结束格式
*/
char* evbuffer_readln(struct evbuffer* buffer,
size_t *n_read_out,
enum evbuffer_eol_style eol_style
);
行结束格式 | |
---|---|
EVBUFFER_EOL_LF | 行尾是单个换行符(也就是\n,ASCII 值是0x0A) |
EVBUFFER_EOL_CRLF_STRICT | 行尾是一个回车符,后随一个换行符(也就是\r\n,ASCII 值是0x0D 0x0A) |
EVBUFFER_EOL_CRLF | 行尾是一个可选的回车,后随一个换行符(也就是说,可以是\r\n 或者\n)。这种格式对于解析基于文本的互联网协议很有用,因为标准通常要求\r\n 的行结束符,而不遵循标准的客户端有时候只使用\n |
EVBUFFER_EOL_ANY | 行尾是任意数量、任意次序的回车和换行符。这种格式不是特别有用。它的存在主要是为了向后兼容。 |
evbuffer之间数据的转移 | |
---|---|
int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src); | 将 src 中的 [所有数据] 转移到 dst 末尾 |
int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer *src); | 将 src 中的 [所有数据] 转移到 dst 之前 |
int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,size_t datlen); | 将 src 中的 [datlen字节的数据] 转移到 dst 末尾 |
向evbuffer中添加数据 | |
---|---|
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); | 将 data 处的 datalen 字节的数据添加到 buf 的末尾 |
int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); | 将 data 处的 datalen 字节的数据添加到 buf 之前 |
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, …); | 添加格式化的数据到 buf 末尾 |
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap); | 添加格式化的数据到 buf 末尾 |
从evbuffer中移除数据 | |
---|---|
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); | 从buf前面复制和移除datlen字节的数据存放到data内存中 |
int evbuffer_drain(struct evbuffer *buf, size_t datlen); | 从buf前面移除datlen字节的数据,不进行复制 |
从 evbuffer 中复制出数据(即获取缓冲区数据的副本),而不移除缓冲区中的数据 | |
---|---|
int evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen); | 从buf前面复制datlen字节的数据存放到data内存中,但不移除数据 |
[ BufferEvent - 数据缓冲 ]
一.bufferevent介绍
- bufferevent由一个底层的传输端口(如套接字),一个读/input缓冲区和一个写/output缓冲区组成。
- 每个 bufferevent 有两个与数据相关的回调:读取回调函数和写入回调函数。 bufferevent中回调函数触发的条件与普通的event回调函数触发的条件不同( 普通的事件在底层传输端口已经就绪,可读或可写时就会执行回调函数,而 bufferevent 是在读取或写入了一定量的数据后才会调用回调函数)
- 也请注意:当前 bufferevent 只能用于像 TCP 这样的面向流的协议,将来才可能会支持像 UDP 这样的面向数据报的协议
- bufferevent 也有“错误事件”回调,用于向应用通知非面向数据的事件,如连接已经关闭或者发生错误。定义了下列事件标志:
事件标志 | |
---|---|
BEV_EVENT_READING | 读取操作时发生某事件,具体是哪种事件请看其他标志 |
BEV_EVENT_WRITING | 写入操作时发生某事件,具体是哪种事件请看其他标志 |
BEV_EVENT_TIMEOUT | 发生超时 |
BEV_EVENT_CONNECTED | 请求的连接过程已经完成 |
BEV_EVENT_EOF | 遇到文件结束指示 |
BEV_EVENT_ERROR | 操 作 时 发 生 错 误 。 关 于 错 误 的 更 多 信 息 , 请 调 用EVUTIL_SOCKET_ERROR() |
二.水位线与回调函数触发情况
1. 读取低水位线/读取高水位线

2. 写入低水位线/写入高水位线
三.延迟回调
默认情况下,在相应的条件发生时,bufferevent的回调函数将会立即被调用。但是,在依赖关系复杂的情况下,这种立即调用会造成麻烦。比如说,假如某个回调函数在evbuffer A空的时候向其中写入数据,而另一个回调函数在evbuffer A满的时候从中取出数据。这些调用都是在栈上发生的,在依赖关系足够复杂的时候,有栈溢出的风险。
==> 要解决此问题,可以请求 bufferevent(或者 evbuffer)延迟其回调。条件满足时,
延迟回调不会立即调用,而是在 event_loop()调用中被排队,然后在通常的事件回调之后执行
四.bufferevent的使用、相关API
bufferevent使用步骤 | |
---|---|
创建基于套接字的bufferevent | struct bufferevent* bufev = bufferevent_socket_new(base,fd,options) |
为bufev设置回调函数 | bufferevent_setcb(bufev,read_cb,write_cb,event_cb,cbarg); |
设置水位线 | bufferevent_setwatermark |
启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或 EV_READ | EV_WRITE事件 | bufferevent_enable/bufferevent_disable |
操作bufferevent中的数据 |
(1) bufferevent_socket_new:创建基于套接字的bufferevent
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options
);
返回值:成功返回一个bufferevent,失败返回NULL
参数
- base:event_base
- fd:表示套接字的文件描述符(如果想以后设置文件描述符,则设置fd为-1)
- options: bufferevent 选项的位掩码(bufferevent选项标志)
options:bufferevent 选项的位掩码 | 含义 |
---|---|
BEV_OPT_CLOSE_ON_FREE 常用 | 释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等 |
BEV_OPT_THREADSAFE | 自动为 bufferevent 分配锁,这样就可以安全地在多个线程中使用 bufferevent |
BEV_OPT_DEFER_CALLBACKS | bufferevent 延迟所有回调 |
BEV_OPT_UNLOCK_CALLBACKS | 默认情况下,如果设置 bufferevent 为线程安全 的,则 bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent 在执行回调的时候不进行锁定 |
(2)bufferevent_free:释放bufferevent
void bufferevent_free(struct bufferevent *bev);
- bufferevent内部具有引用计数 (如果释放时还有未决的延迟回调,则在回调完成之前bufferevent不会被删除)
- 如果设置了 BEV_OPT_CLOSE_ON_FREE 标志,并且 bufferevent 有一个套接字或者底层 bufferevent 作为其传输端口,则释放 bufferevent 将关闭这个传输端口
(3) 设置[水位]
设置bufev的读取水位、写入水位,或者二者同时设置。
[1] 如果events参数设置了EV_READ,调整读取水位
[2] 如果events参数设置了EV_WRITE,调整写入水位
其中,对于高水位,0表示“无限”
void bufferevent_setwatermark(
struct bufferevent *bufev,
short events,
size_t lowmark,
size_t highmark
);
(4)设置/获取 [回调函数]
(4-1)回调函数类型
当相应的事件到来时,会触发相应的回调函数,此时
回调函数的参数
已经会被
自动赋值
typedef void (*bufferevent_data_cb)(
struct bufferevent *bev, //触发了的事件bufferevent
void *ctx //调用bufferevent_setcb()时用户提供的 cbarg 参数
);
typedef void (*bufferevent_event_cb)(
struct bufferevent *bev,
short events, //一个表示事件标志的位掩码
void *ctx
);
(4-2)设置/获取bufferevent的回调函数、参数
void bufferevent_setcb( //给bufev设置回调函数、回调函数参数
struct bufferevent *bufev,
bufferevent_data_cb readcb, //已经读取足够的数据时被回调
bufferevent_data_cb writecb, //已经写入足够的数据时被回调
bufferevent_event_cb eventcb, //发生错误时被回调
void *cbarg //用户提供的 cbarg 参数
);
void bufferevent_getcb( //获取bufev的回调函数、回调函数参数
struct bufferevent *bufev,
bufferevent_data_cb *readcb_ptr,
bufferevent_data_cb *writecb_ptr,
bufferevent_event_cb *eventcb_ptr,
void **cbarg_ptr
);
(5) 启用bufferevent_enable / 禁用bufferevent_disable
- 默认情况下,新创建的bufferevent的写入EV_WRITE 是启用的,读取EV_READ是没有启用的。
- 可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。
- 没有启用读取或者写入事件时, bufferevent 将不会试图进行数据读取或者写入。
void bufferevent_enable( //启用bufev上的events事件
struct bufferevent *bufev,
short events
);
void bufferevent_disable( //禁用bufev上的events事件
struct bufferevent *bufev,
short events
);
short bufferevent_get_enabled( //获取 bufferevent 上当前启用的事件
struct bufferevent *bufev
);
(6)操作bufferevent中的数据
(6.1) 向bufferevent的输出缓冲区添加数据
成功时,都返回 0;发生错误时,则返回-1
//从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
);
(6.2) 从bufferevent的输入缓冲区移除数据
//至多从输入缓冲区bufev移除 size 字节的数据,将其存储到内存中 data 处
//返回值:实际移除的字节数
//注意,对于 bufferevent_read(),data 处的内存块必须有足够的空间容纳 size 字节数据。
size_t bufferevent_read(
struct bufferevent *bufev,
void *data,
size_t size
);
//抽空输入缓冲区bufev的所有内容,将其放置到 buf 中
//成功时,都返回 0;发生错误/失败时,则返回-1
int bufferevent_read_buffer(
struct bufferevent *bufev,
struct evbuffer *buf
);
(7)bufferevent的清空操作
- bufferevent_flush()相当于fflush()刷新输出缓冲区中的数据,使数据显示到标准输出上。
- 但是bufferevent_flush()函数的功能更加强大,bufferevent_flush()函数既可以刷新输出缓冲区中的数据,又可以刷新输入缓冲区中的数据
- bufferevent_flush()函数刷新缓冲区的功能,可以强制从底层传输端口写入或读取数据,不受水位线的限制
int bufferevent_flush(
struct bufferevent *bufev,
short iotype,
enum bufferevent_flush_mode state
);
返回值
- 失败:-1
- 成功,且没有数据被清空:0
- 成功,且有数据被清空:1
参数
- iotype:EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE, 用于指示应该处理读取、写入, 还是二者都处理
- state:可以是 BEV_NORMAL、BEV_FLUSH 或者 BEV_FINISHED
- BEV_NORMAL 和 BEV_FLUSH 的区别依赖于具体的bufferevent 类型
- BEV_FINISHED指示应该告知另一端,没有更多数据需要发送了