bufferevent filter 过滤器简介
bufferevent filter 过滤器可以在 读取前和写入后对数据进行一系列的预处理操作,比如压缩和加密。对用户来讲,过滤器就像一个黑盒子,用户仍然只需要完成读取和写入,而不必知晓过滤器中的操作。bufferevent filter 的数据流如下图:
underlying bufferevent 表示潜在的、根本的 bufferevent,即用户处理读写操作的 bufferevent,而 filter bufferevent 是在此基础上创建的,后面的代码会有详细说明。读取和写入(接收和发送)的数据流刚好相反:
- 读取(接收)的数据先进入过滤器中,在过滤器处理完成后再传递给用户进行读取,即 过滤在读取之前 ,例如压缩数据要先在 filter 中解压,然后在 underlying 读取解压后的原始;
- 写入(发送)的数据由用户发起,再传给过滤器,过滤器处理完后再发送出去,即 过滤在写入之后 ,例如在 underlying 中发送的原始数据,会在 filter 中进行压缩后再发送。
bufferevent filter 过滤器 API 分析
要使用过滤器,需要先明白 bufferevent_filter_new
函数和结构体 evbuffer
。
-
bufferevent_filter_new 函数定义
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);
回调函数
bufferevent_filter_cb
的定义如下/** * @brief: 回调函数,用于实现 bufferevent 的过滤器 * * @param src: 源事件缓存 * @param dst: 目的事件缓存 * @param dst_limit: 写入 dst 的字节上限,-1表示无限制 * @param mode: 数据刷新模式 * @param ctx: 用户传递的参数 * * @return: 返回过滤器的处理结果,详见后面说明 */ typedef enum bufferevent_filter_result (*bufferevent_filter_cb)( struct evbuffer *src, struct evbuffer *dst, ev_ssize_t dst_limit, enum bufferevent_flush_mode mode, void *ctx); // filter 回调函数返回值 enum bufferevent_filter_result { // 正常 BEV_OK = 0, // 还需要更多的数据才能输出 BEV_NEED_MORE = 1, // filter 发生错误,无法进一步处理数据 BEV_ERROR = 2 }; // filter 数据刷新模式 enum bufferevent_flush_mode { // 正常处理数据 BEV_NORMAL = 0, // 检查发送的所有数据 BEV_FLUSH = 1, // 读完或写完数据后,会有 EOF 标志 BEV_FINISHED = 2 };
-
evbuffer 是一个不透明的结构体,即只有声明,定义不对外公开,用于在 bufferevent 上发送或接收的数据,其实这个结构体就是在 filter bufferevent 和 underlying bufferevent 之间传递数据用的。在过滤器中,常用的与之相关的函数有
evbuffer_add
和evbuffer_remove
,分别表示在 evbuffer 中添加数据和读取数据,函数定义如下/** * @breif: 添加数据到 evbuffer 的结尾处 * * @param buf: 待添加数据的 evbuffer 对象 * @param data: 数据指针 * @param datlen: 数据长度,单位 byte * * @return: 成功返回0,失败返回-1 */ int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); /** * @brief: 从 evbuffer 的开始处读取指定长度的数据 * 若 evbuffer 中的数据不足指定长度,则尽可能多的读取数据 * * @param buf: 待读取数据的 evbuffer 对象 * @param data: 数据指针 * @param datlen: 数据长度,单位 byte * * @return: 成功返回读取的字节数,失败返回-1 */ int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
简单来说,evbuffer_add 类似向队尾插入数据,evbuffer_remove 类似从队头读取数据。事实上它内部就是链表实现的,我在网上收到一位大神画的 evbuffer 结构体图,在此膜拜一下here 。
buffervent 和 evbuffer 经常一起使用,但也有区别
- bufferevent 是一个事件缓冲 I/O,内部实现了基本 socket recv/send 操作;
- evbuffer 只是一个数据缓冲区,使用链表实现,不带任何 I/O 操作。