数据封装evBuffer、数据缓冲Bufferevent

[ 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

注意:

  1. bufferevent_get_input/bufferevent_get_output只是获取输入/输出缓冲区中的数据,并没有将数据从evbuffer中移走。
  2. 而,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介绍

  1. bufferevent由一个底层的传输端口(如套接字),一个读/input缓冲区和一个写/output缓冲区组成。
  2. 每个 bufferevent 有两个与数据相关的回调:读取回调函数和写入回调函数。 bufferevent中回调函数触发的条件与普通的event回调函数触发的条件不同( 普通的事件在底层传输端口已经就绪,可读或可写时就会执行回调函数,而 bufferevent 是在读取或写入了一定量的数据后才会调用回调函数)
  3. 也请注意:当前 bufferevent 只能用于像 TCP 这样的面向流的协议,将来才可能会支持像 UDP 这样的面向数据报的协议
  4. 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使用步骤
创建基于套接字的buffereventstruct 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_CALLBACKSbufferevent 延迟所有回调
BEV_OPT_UNLOCK_CALLBACKS默认情况下,如果设置 bufferevent 为线程安全 的,则 bufferevent 会在调用用户提供的回调时进行锁定。设置这个选项会让 libevent 在执行回调的时候不进行锁定

(2)bufferevent_free:释放bufferevent

void bufferevent_free(struct bufferevent *bev);

  1. bufferevent内部具有引用计数 (如果释放时还有未决的延迟回调,则在回调完成之前bufferevent不会被删除)
  2. 如果设置了 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

  1. 默认情况下,新创建的bufferevent的写入EV_WRITE 是启用的,读取EV_READ是没有启用的
  2. 可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。
  3. 没有启用读取或者写入事件时, 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的清空操作

  1. bufferevent_flush()相当于fflush()刷新输出缓冲区中的数据,使数据显示到标准输出上。
  2. 但是bufferevent_flush()函数的功能更加强大,bufferevent_flush()函数既可以刷新输出缓冲区中的数据,又可以刷新输入缓冲区中的数据
  3. 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指示应该告知另一端,没有更多数据需要发送了
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值