当自己看别人翻译过来的图书时,我们有时会经常骂骂咧咧道:“翻译真烂”!可是当我自己尝试去翻译这篇文档时,才知道翻译真的不是一种简单的事情。翻译的不好,所以敬请读者见谅,如果在哪看到比这篇翻译的更好的,敬请告知!谢谢!
Libevent参考手册第七章:Evbuffers:用于缓冲IO的实用工具
Nick Mathewson 著
老衣 翻译
Libevent的evbuffer功能通过实现一个字节队列,在队列末尾添加数据,在队列头移除数据。
Evbuffers 是通常用来做缓冲网络 IO 的"缓冲区"部分。他们将不会提供安排 IO 或IO准备就绪时的触发等功能: 而这些功能是bufferevents应该做的。
本章中的函数声明都在event2/buffer.h 中,除非另有说明。
创建或释放 evbuffer
接口
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *buf);
这些函数应该是比较清晰: evbuffer_new() 分配并返回一个新的空的 evbuffer , evbuffer_free() 函数删除所有的内容。
这些函数从Libevent 0.8版本开始存在。
Evbuffers 和线程安全
接口
int evbuffer_enable_locking(struct evbuffer *buf, void *lock);
void evbuffer_lock(struct evbuffer *buf);
void evbuffer_unlock(struct evbuffer *buf);
默认情况下,多线程同时访问evbuffer是不安全的。如果您需要执行此操作,您可以在evbuffer上调用 evbuffer_enable_locking()函数。如果此函数的lock参数为 NULL,则 Libevent 通过evthread_set_lock_creation_callback函数分配一把新锁。否则,它所使用的参数作为该锁。
Evbuffer_lock() 和 evbuffer_unlock() 函数的功能分别是获得或者释放evbuffer 上的锁。您可以使用它们进行一组原子操作。如果evbuffer上没有使用锁,这些函数什么也不做。
(请注意您不需要调用 evbuffer_lock() 和 evbuffer_unlock() 在单个操作上: 单个操作已经是原子操作了。当你有一个以上的操作需要执行时,你只需手动锁定 evbuffer,而不需要引入另一个线程。)
这些函数在Libevent 2.0.1-alpha版本中引入。
检查 evbuffer
接口
size_t evbuffer_get_length(const struct evbuffer *buf);
此函数返回存储在 evbuffer 中的字节的数。
它是在Libevent 2.0.1-alpha版本中引入。
接口
size_t evbuffer_get_contiguous_space(const struct evbuffer *buf);
此函数返回evbuffer的前面的连续存储的字节数。在 evbuffer 中的字节可能存储在多个单独的大块的内存 ;此函数返回第一个文本块中当前存储的字节的数。
它是在Libevent 2.0.1-alpha版本中引入。
将数据添加到 evbuffer: 基础知识
接口
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
此函数把存储在data中的datlen个字节的数据追加到buf上,成功返回0,失败返回-1。
接口
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap);
这些函数把格式化得数据追加到buf的末尾。处理格式参数和其他余下的参数与C 库中"printf"和"vprintf"函数相似。这些函数将返回附加的字节的数。
接口
int evbuffer_expand(struct evbuffer *buf, size_t datlen);
此函数改变内存缓冲区中的最后一个区块,或添加新的区块,以致现在的缓冲区足够大能够容纳 datlen个字节无需再次进行分配。
示例
/* Here are two ways to add "Hello world 2.0.1" to a buffer. */
/* Directly: */
evbuffer_add(buf, "Hello world 2.0.1", 17);
/* Via printf: */
evbuffer_add_printf(buf, "Hello %s %d.%d.%d", "world", 2, 0, 1);
Evbuffer_add() 和 evbuffer_add_printf() 函数在 Libevent 0.8中引入的。evbuffer_expand() 是在 0.9, evbuffer_add_printf() 首次出现在 Libevent 1.1。
将数据从一个 evbuffer 移动到另一个
为了提高效率,Libevent 已经优化了用于将数据从一个 evbuffer 移动到另一个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_add_buffer() 函数将从src的所有的数据移动到dst的尾端。成功返回0失败则返回-1。
Dst,复制尽可能少的末尾,evbuffer_remove_buffer() 函数移动src中datlen个字节的数据到Dst的末尾,尽量少复制。它返回移动的字节的数。
我们推出了 在Libevent 0.8版本中引入evbuffer_add_buffer()函数,evbuffer_remove_buffer() 是 Libevent 2.0.1-alpha 中的新功能。
将数据添加到前面的 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() 一样,只是他们将数据移动到目标缓冲区的前面。
应谨慎使用这些函数,决不与一bufferevent共享一evbuffer。他们是 Libevent 2.0.1-alpha 中的新功能。
重新排列 evbuffer 的内部的布局
有时你想偷看 evbuffer前N个字节的数据,并将其视为连续的字节数组。为此,您必须首先确保前面的缓冲区真的连续。
接口
unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);
evbuffer_pullup() 函数"线性化"buf的前size个字节,复制或移动它们,以确保它们都连续和占用的内存的同一块。如果参数size为负数,则函数 “线性化” 整个缓冲区。如果参数size大于缓冲区中的字节数,则此函数返回 NULL。否则,evbuffer_pullup() 返回 buf 中第一个字节的指针。
,若参数size比较大,调用 evbuffer_pullup()的运行速度可能会较慢,因为它可能需要将整个缓冲区的内容复制。
示例
#include <event2/buffer.h>
#include <event2/util.h>
#include <string.h>
int parse_socks4(struct evbuffer *buf, ev_uint16_t *port, ev_uint32_t *addr)
{
/* Let's parse the start of a SOCKS4 request! The format is easy:
* 1 byte of version, 1 byte of command, 2 bytes destport, 4 bytes of
* destip. */
unsigned char *mem;
mem = evbuffer_pullup(buf, 8);
if (mem == NULL) {
/* Not enough data in the buffer */
return 0;
} else if (mem[0] != 4 || mem[1] != 1) {
/* Unrecognized protocol or command */
return -1;
} else {
memcpy(port, mem+2, 2);
memcpy(addr, mem+4, 4);
*port = ntohs(*port);
*addr = ntohl(*addr);
/* Actually remove the data from the buffer now that we know we
like it. */
evbuffer_drain(buf, 8);
return 1;
}
}
请注意
若evbuffer_pullup() 中的参数size等于evbuffer_get_contiguous_space() 所返回的值,则调用evbuffer_pullup()函数不会导致任何数据被复制或者移动。
Evbuffer_pullup()