evbuffer 实现一个字节队列,优化了数据添加和删除。
一、创建或释放evbuffer
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *buf);
二、
evbuffer 与线程安全
多个线程同时访问evbuffer 是不安全的,需要用到evbuffer_enable_locking。如果lock为NULL,则该函数用evthread_set_lock_creation_callback提供的函数创建一个锁。int evbuffer_enable_locking(struct evbuffer *buf, void *lock); void evbuffer_lock(struct evbuffer *buf); void evbuffer_unlock(struct evbuffer *buf);
三、检查evbufferevbuffer_lock和evbuffer_unlock分别对
evbuffer 加锁和解锁。因为单个操作已经是原子操作,所以你不需要为单个evbuffer 操作加锁,除非需要做多个操作而不让其他线程访问。
size_t evbuffer_get_length(const struct evbuffer *buf);
返回buf存储的字节数。size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);
返回目前存储在第一个内存块的字节数。四、向evbuffer 添加数据
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);
int evbuffer_expand(struct evbuffer *buf, size_t datlen);
五、evbuffer 间的数据移动int evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src);
int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
size_t datlen);
六、在evbuffer 前面添加数据int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size);
int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src);
这两个函数与evbuffer_add() 类似evbuffer_add_buffer(),只不过它们是在buf的前面添加数据。
七、重排evbuffer 的内部布局
有时候我们需要查询buf的前N个字节,并认为它是连续的。此时就需要确保buf前面的数据要是连续的。
unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);
该函数通过移动或拷贝数据确保buf前size个字节数据是线性化的。如果size为负,则线性化整个buf,如果size大于buf的字节数,则返回NULL。否则函数返回指向buf第一个字节 的指针。八、从evbuffer 删除数据
int evbuffer_drain(struct evbuffer *buf, size_t len);
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);
evbuffer_remove()从buf前面移除datlen个字节的数据,并将数据拷贝到data。
evbuffer_drain与evbuffer_remove()类似,只是它不拷贝数据。
九、从evbuffer 把数据拷贝出来
有时候我们想把数据从evbuffer 拷贝出来,但是又不行删除这些数据。
evbuffer_copyout()与evbuffer_remove()类似,只是它不删除数据。它把buf前datlen个字节的数据拷贝到data。ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data, size_t datlen); ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen);
evbuffer_copyout_from() 和 evbuffer_copyout()一样,只是它从pos位置开始拷贝数据。
十、基于行的输入
许多互联网协议使用了基于行的格式。该函数从buffer读取一行数据新分配的一空字符结尾的字符串。如果enum evbuffer_eol_style { EVBUFFER_EOL_ANY, EVBUFFER_EOL_CRLF, EVBUFFER_EOL_CRLF_STRICT, EVBUFFER_EOL_LF, EVBUFFER_EOL_NUL }; char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, enum evbuffer_eol_style eol_style);
n_read_out不为空,则设置为返回的字节数。如果没有一个整行可以读,则函数返回NULL。
十一、在evbuffer内搜索
struct evbuffer_ptr { ev_ssize_t pos;
struct { /* internal fields */ } _internal; };pos表示的是evbuffer 与开始位置的偏移
struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start); 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); 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 }; int evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *pos, size_t position, enum evbuffer_ptr_how how);
evbuffer_ptr_set
操作
buffer中evbuffer_ptr的pos,如果how为EVBUFFER_PTR_SET,则指针移到position,如果how为EVBUFFER_PTR_ADD,则指针向前移动position个字节。
十二、不用拷贝的检查数据
调用该函数时,你传入一组长度为vec_out的evbuffer_iovec数组,数组每个元素的iov_base设置为一个内存块的地址,iov_len为内存块的大小。struct evbuffer_iovec { void *iov_base; size_t iov_len; }; int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, struct evbuffer_ptr *start_at, struct evbuffer_iovec *vec_out, int n_vec);
十三、直接给evbuffer添加数据
int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, struct evbuffer_iovec *vec, int n_vecs); int evbuffer_commit_space(struct evbuffer *buf, struct evbuffer_iovec *vec, int n_vecs);
一旦调用evbuffer_commit_space(),你写入vec数组的数据将提交为buf的数据。evbuffer_reserve_space扩张buf,使得大小至少为size。扩张的内存块的指针和块大小存储在数组vec里,
n_vecs是数组的元素个数。
十四、网络IO evbufferint evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, ev_ssize_t howmuch); int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);
evbuffer_read从fd读取最多
howmuch个字节到buffer的结尾。evbuffer_write_atmost尝试最多写howmuch个字节到fd。
evbuffer_write() 与给evbuffer_write_atmost传入负的howmuch一样。将尝试把全部数据写入fd。十五、evbuffer和回调函数用户经常需要知道什么时候给buffer添加或删除数据。因此,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_add_cb()给buffer添加一个回调函数。一个evbuffer 可以有多个回调函数。struct evbuffer_cb_entry; struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg);
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); #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);
十六、基于evbuffer的IO的避免数据拷贝int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base);
非常快的网络编程都尽可能的少的数据拷贝,libevent提供这样的机制来实现。该函数通过引用在evbuffer末尾添加数据,不用拷贝。evbuffer 只需要保存data的指针。因此,evbuffer 还在使用的时候必须保证指针是有效的。当不再需要这些数据时,则可调用cleanupfn函数。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 添加文件有的操作系统提供不需要把文件拷贝到用户空间就能写到网络的方法。该函数假定有一个打开并且有效的文件fd,int evbuffer_add_file(struct evbuffer *output, int fd, ev_off_t offset, size_t length);
它从文件的offset位置读取
length个字节到
output。
十八、细粒度的文件片段控制
evbuffer_add_file()多次添加同一个文件时效率比较低。struct evbuffer_file_segment; 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_file_segment_new()返回一个新的evbuffer_file_segment 来体现文件fd的数据段。有了文件数据段之后就可以调用evbuffer_add_file_segment()添加到
evbuffer。如果不再需要这些文件数据段,则可以用evbuffer_file_segment_free()释放。
你也可以给文件数据段添加回调函数,当最后一个引用该数据段的释放就调用回调函数。回调函数不应试图再去用该数据段。typedef void (*evbuffer_file_segment_cleanup_cb)( struct evbuffer_file_segment const *seg, int flags, void *arg); void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, evbuffer_file_segment_cleanup_cb cb, void *arg);
十九、通过引用把evbuffer添加到另外一个evbuffer
int evbuffer_add_buffer_reference(struct evbuffer *outbuf, struct evbuffer *inbuf);
二十、使
evbuffer 只能添加或删除
int evbuffer_freeze(struct evbuffer *buf, int at_front); int evbuffer_unfreeze(struct evbuffer *buf, int at_front);