转载请注明出处:http://blog.csdn.net/luotuo44/article/details/39325447
锁操作:
在 前一篇博文
可以看到很多函数在操作前都需要对这个evbuffer进行加锁。同event_base不同,如果evbuffer支持锁的话,要显式地调用函数evbuffer_enable_locking。
-
- int
- evbuffer_enable_locking(struct evbuffer *buf, void *lock)
- {
- #ifdef _EVENT_DISABLE_THREAD_SUPPORT
- return -1;
- #else
- if (buf->lock)
- return -1;
-
- if (!lock) {
-
- EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
- if (!lock)
- return -1;
- buf->lock = lock;
-
- buf->own_lock = 1;
- } else {
- buf->lock = lock;
- buf->own_lock = 0;
- }
-
- return 0;
- #endif
- }
可以看到,第二个参数可以为NULL。此时函数内部会申请一个锁。明显如果要让evbuffer能使用锁,就必须在一开始就调用evthread_use_windows_threads()或者evthread_use_pthreads(),关于这个可以参考一篇博文。
因为用户操控这个evbuffer,所以Libevent提供了加锁和解锁接口给用户使用。
-
- void
- evbuffer_lock(struct evbuffer *buf)
- {
- EVBUFFER_LOCK(buf);
- }
-
- void
- evbuffer_unlock(struct evbuffer *buf)
- {
- EVBUFFER_UNLOCK(buf);
- }
-
-
- #define EVBUFFER_LOCK(buffer) \
- do { \
- EVLOCK_LOCK((buffer)->lock, 0); \
- } while (0)
- #define EVBUFFER_UNLOCK(buffer) \
- do { \
- EVLOCK_UNLOCK((buffer)->lock, 0); \
- } while (0)
在Libevent内部,一般不会使用这个两个接口,而是直接使用EVBUFFER_LOCK(buf)和EVBUFFER_UNLOCK(buf)。
查找操作:
下图展示了evbuffer的一些查找操作以及调用关系。
查找结构体:
对于一个数组或者一个文件,只需一个下标或者偏移量就可以定位查找了。但对于evbuffer来说,它的数据是由一个个的evbuffer_chain用链表连在一起的。所以在evbuffer中定位,不仅仅要有一个偏移量,还要指明是哪个evbuffer_chain,甚至是在evbuffer_chain中的偏移量。因此Libevent定义了一个查找(定位)结构体:
-
- struct evbuffer_ptr {
- ev_ssize_t pos;
-
-
- struct {
- void *chain;
- size_t pos_in_chain;
- } _internal;
- };
有一点要注意,pos_in_chain是从misalign这个错开空间之后计算的,也就是说其实际偏移量为:chain->buffer+ chain->misalign + pos_in_chain。
定位结构体有一个对应的操作函数evbuffer_ptr_set,该函数就像fseek函数那样,可以设置或者移动偏移量,并且可以绝对和相对地移动。
-
- enum evbuffer_ptr_how {
- EVBUFFER_PTR_SET,
- EVBUFFER_PTR_ADD
- };
-
-
-
-
-
-
- int
- evbuffer_ptr_set(struct evbuffer *buf, struct evbuffer_ptr *pos,
- size_t position, enum evbuffer_ptr_how how)
- {
- size_t left = position;
- struct evbuffer_chain *chain = NULL;
-
- EVBUFFER_LOCK(buf);
-
-
- switch (how) {
- case EVBUFFER_PTR_SET:
- chain = buf->first;
- pos->pos = position;
- position = 0;
- break;
- case EVBUFFER_PTR_ADD:
- chain = pos->_internal.chain;
- pos->pos += position;
- position = pos->_internal.pos_in_chain;
- break;
- }
-
-
- while (chain && position + left >= chain->off) {
- left -= chain->off - position;
- chain = chain->next;
- position = 0;
- }
-
-
- if (chain) {
- pos->_internal.chain = chain;
- pos->_internal.pos_in_chain = position + left;
- } else {
- pos->_internal.chain = NULL;
- pos->pos = -1;
- }
-
- EVBUFFER_UNLOCK(buf);
-
- return chain != NULL ? 0 : -1;
- }
可以看到,该函数只考虑了向后面的chain移动定位指针,不能向。当然如果参数position小于0,并且移动时并不会跨越当前的chain,还是可以的。不过最好不要这样做。如果确实想移回头,那么可以考虑下面的操作。
- pos.position -= 20;
- evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET);
查找一个字符:
有下面代码的前一个函数是可以用来查找一个字符的,第二个函数则是获取对于位置的字符。
- static inline ev_ssize_t
- evbuffer_strchr(struct evbuffer_ptr *it, const char chr);
-
- static inline char
- evbuffer_getchr(struct evbuffer_ptr *it);
-
- static inline int
- evbuffer_strspn(struct evbuffer_ptr *ptr, const char *chrset);
函数evbuffer_strchr是从it指向的evbuffer_chain开始查找,会往后面的链表查找。it是一个值-结果参数,如果查找到了,那么it将会指明被查找字符的位置,并返回相对于evbuffer的总偏移量(即it->pos)。如果没有找到,就返回-1。由于实现都是一般的字符比较,所以就不列出代码了。函数evbuffer_getchr很容易理解也不列出代码了。
第三个函数evbuffer_strspn的参数chrset虽然是一个字符串,其实内部也是比较字符的。该函数所做的操作和C语言标准库里面的strspn函数是一样的。这里也不多说了。关于strspn函数的理解可以查看 这里
。
查找一个字符串:
字符串的查找函数有evbuffer_search_range和evbuffer_search,后者调用前者完成查找。
在讲查找前,先看一个字符串比较函数evbuffer_ptr_memcmp。该函数是比较某一个字符串和从evbuffer中某个位置开始的字符是否相等。明显比较的时候需要考虑到跨evbuffer_chain的问题。
- static int
- evbuffer_ptr_memcmp(const struct evbuffer *buf, const struct evbuffer_ptr *pos,
- const char *mem, size_t len)
- {
- struct evbuffer_chain *chain;
- size_t position;
- int r;
-
-
- if (pos->pos + len > buf->total_len)
- return -1;
-
-
- chain = pos->_internal.chain;
- position = pos->_internal.pos_in_chain;
- while (len && chain) {
- size_t n_comparable;
- if (len + position > chain->off)
- n_comparable = chain->off - position;
- else
- n_comparable = len;
- r = memcmp(chain->buffer + chain->misalign + position, mem,
- n_comparable);
- if (r)
- return r;
-
-
- mem += n_comparable;
- len -= n_comparable;
- position = 0;
- chain = chain->next;
- }
-
- return 0;
- }
该函数首先比较pos指向的当前evbuffer_chain,如果字符mem还有一些字符没有参与比较,那么就需要用下一个evbuffer_chain的数据。
由于evbuffer的数据是由链表组成的,没办法直接用KMP查找算法或者直接调用strstr函数。有了evbuffer_ptr_memcmp函数,读者可能会想,一个字节一个字节地挪动evbuffer的数据,依次调用evbuffer_ptr_memcmp函数。但evbuffer_search_range函数也不是直接调用函数evbuffer_ptr_memcmp的。而是先用字符查找函数,找到要查找字符串中的第一个字符,然后才调用那个函数。下面是具体的代码。
-
- struct evbuffer_ptr
- evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start)
- {
- return evbuffer_search_range(buffer, what, len, start, NULL);
- }
-
-
- 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 pos;
- struct evbuffer_chain *chain, *last_chain = NULL;
- const unsigned char *p;
- char first;
-
- EVBUFFER_LOCK(buffer);
-
-
- if (start) {
- memcpy(&pos, start, sizeof(pos));
- chain = pos._internal.chain;
- } else {
- pos.pos = 0;
- chain = pos._internal.chain = buffer->first;
- pos._internal.pos_in_chain = 0;
- }
-
- if (end)
- last_chain = end->_internal.chain;
-
- if (!len || len > EV_SSIZE_MAX)
- goto done;
-
- first = what[0];
-
-
-
-
-
-
- while (chain) {
- const unsigned char *start_at =
- chain->buffer + chain->misalign +
- pos._internal.pos_in_chain;
-
-
-
- p = memchr(start_at, first,
- chain->off - pos._internal.pos_in_chain);
- if (p) {
- pos.pos += p - start_at;
- pos._internal.pos_in_chain += p - start_at;
-
-
-
-
-
- if (!evbuffer_ptr_memcmp(buffer, &pos, what, len)) {
-
- if (end && pos.pos + (ev_ssize_t)len > end->pos)
- goto not_found;
- else
- goto done;
- }
-
-
- ++pos.pos;
- ++pos._internal.pos_in_chain;
-
-
- if (pos._internal.pos_in_chain == chain->off) {
- chain = pos._internal.chain = chain->next;
- pos._internal.pos_in_chain = 0;
- }
- } else {
- if (chain == last_chain)
- goto not_found;
-
-
- pos.pos += chain->off - pos._internal.pos_in_chain;
- chain = pos._internal.chain = chain->next;
- pos._internal.pos_in_chain = 0;
- }
- }
-
- not_found:
- pos.pos = -1;
- pos._internal.chain = NULL;
- done:
- EVBUFFER_UNLOCK(buffer);
- return pos;
- }
evbuffer_ptr_memcmp函数和evbuffer_search函数是有区别的。前者只会比较从pos指定位置开始的字符串,不会在另外的地方找一个字符串。而后者则会在后面另外找一个字符串进行比较。
查找换行符:
换行符是一个比较重要的符号,例如http协议就基于行的。Libevent实现了一个简单的http服务器,因此在内部Libevent实现了一些读取一行数据函数以及与行相关的操作。
有些系统行尾用\r\n有些则直接用\n,这些不统一给编程造成了一些麻烦。因此在Libevent中定义了一个枚举类型,专门来用表示eol(end of line)的。
- EVBUFFER_EOL_LF:行尾是’\n’字符
- EVBUFFER_EOL_CRLF_STRICT:行尾是”\r\n”,一个回车符一个换行符
- EVBUFFER_EOL_CRLF:行尾是”\r\n”或者’\n’。这个是很有用的,因为可能标准的协议里面要求”\r\n”,但一些不遵循标准的用户可能使用’\n’
- EVBUFFER_EOL_ANY:行尾是任意次序或者任意数量的’\r’或者’\n’。这种格式不是很有用,只是用来向后兼容而已
函数evbuffer_readln是用来读取evbuffer中的一行数据(不会读取行尾符号)。
- enum evbuffer_eol_style {
- EVBUFFER_EOL_ANY,
- EVBUFFER_EOL_CRLF,
- EVBUFFER_EOL_CRLF_STRICT,
- EVBUFFER_EOL_LF
- };
-
-
-
- char *
- evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out,
- enum evbuffer_eol_style eol_style)
- {
- struct evbuffer_ptr it;
- char *line;
- size_t n_to_copy=0, extra_drain=0;
- char *result = NULL;
-
- EVBUFFER_LOCK(buffer);
-
- if (buffer->freeze_start) {
- goto done;
- }
-
-
-
-
- it = evbuffer_search_eol(buffer, NULL, &extra_drain, eol_style);
- if (it.pos < 0)
- goto done;
- n_to_copy = it.pos;
-
- if ((line = mm_malloc(n_to_copy+1)) == NULL) {
- event_warn("%s: out of memory", __func__);
- goto done;
- }
-
-
- evbuffer_remove(buffer, line, n_to_copy);
- line[n_to_copy] = '\0';
-
-
- evbuffer_drain(buffer, extra_drain);
- result = line;
- done:
- EVBUFFER_UNLOCK(buffer);
-
- if (n_read_out)
- *n_read_out = result ? n_to_copy : 0;
-
- return result;
- }
在函数内部会申请空间,并且从evbuffer中提取出一行数据。下面看看函数evbuffer_search_eol是怎么实现的。该函数执行查找行尾的工作,它在内部区分4种不同的行尾类型。
- struct evbuffer_ptr
- evbuffer_search_eol(struct evbuffer *buffer,
- struct evbuffer_ptr *start, size_t *eol_len_out,
- enum evbuffer_eol_style eol_style)
- {
- struct evbuffer_ptr it, it2;
- size_t extra_drain = 0;
- int ok = 0;
-
- EVBUFFER_LOCK(buffer);
-
- if (start) {
- memcpy(&it, start, sizeof(it));
- } else {
- it.pos = 0;
- it._internal.chain = buffer->first;
- it._internal.pos_in_chain = 0;
- }
-
- switch (eol_style) {
- case EVBUFFER_EOL_ANY:
- if (evbuffer_find_eol_char(&it) < 0)
- goto done;
- memcpy(&it2, &it, sizeof(it));
-
-
-
- extra_drain = evbuffer_strspn(&it2, "\r\n");
- break;
- case EVBUFFER_EOL_CRLF_STRICT: {
- it = evbuffer_search(buffer, "\r\n", 2, &it);
- if (it.pos < 0)
- goto done;
- extra_drain = 2;
- break;
- }
- case EVBUFFER_EOL_CRLF:
- while (1) {
-
-
-
-
- if (evbuffer_find_eol_char(&it) < 0)
- goto done;
- if (evbuffer_getchr(&it) == '\n') {
- extra_drain = 1;
- break;
- } else if (!evbuffer_ptr_memcmp(
- buffer, &it, "\r\n", 2)) {
- extra_drain = 2;
- break;
- } else {
-
- if (evbuffer_ptr_set(buffer, &it, 1,
- EVBUFFER_PTR_ADD)<0)
- goto done;
- }
- }
- break;
- case EVBUFFER_EOL_LF:
- if (evbuffer_strchr(&it, '\n') < 0)
- goto done;
- extra_drain = 1;
- break;
- default:
- goto done;
- }
-
- ok = 1;
- done:
- EVBUFFER_UNLOCK(buffer);
-
- if (!ok) {
- it.pos = -1;
- }
- if (eol_len_out)
- *eol_len_out = extra_drain;
-
- return it;
- }
回调函数:
evbuffer有一个回调函数队列成员callbacks,向evbuffer删除或者添加数据时,就会调用这些回调函数。之所以是回调函数队列,是因为一个evbuffer是可以添加多个回调函数的,而且同一个回调函数可以被添加多次。
使用回调函数时有一点要注意:因为当evbuffer被添加或者删除数据时,就会调用这些回调函数,所以在回调函数里面不要添加或者删除数据,不然将导致递归,死循环。
evbuffer的回调函数对bufferevent来说是非常重要的,bufferevent的一些重要功能都是基于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);
-
-
- typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg);
-
-
-
- struct evbuffer_cb_entry {
-
- TAILQ_ENTRY(evbuffer_cb_entry) next;
-
-
-
- union {
- evbuffer_cb_func cb_func;
- evbuffer_cb cb_obsolete;
- } cb;
- void *cbarg;
- ev_uint32_t flags;
- };
-
- struct evbuffer {
- ...
-
- TAILQ_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;
- };
设置回调函数:
下面看一下怎么设置回调函数。
- struct evbuffer_cb_entry *
- evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
- {
- struct evbuffer_cb_entry *e;
- if (! (e = mm_calloc(1, sizeof(struct evbuffer_cb_entry))))
- return NULL;
- EVBUFFER_LOCK(buffer);
- e->cb.cb_func = cb;
- e->cbarg = cbarg;
- e->flags = EVBUFFER_CB_ENABLED;
- TAILQ_INSERT_HEAD(&buffer->callbacks, e, next);
- EVBUFFER_UNLOCK(buffer);
- return e;
- }
参数cbarg就是回调函数被调用时的那个arg参数,这点对于熟悉Libevent的读者应该不难理解。上面这个函数是被一个evbuffer_cb_entry结构体指针插入到callbacks队列的前面,有关TAILQ_HEAD队列和相关的插入操作可以参考博文《TAILQ_QUEUE队列》。
上面函数返回一个evbuffer_cb_entry结构体指针。用户可以利用这个返回的结构体作一些处理,因为这个结构体已经和添加的回调函数绑定了。比如可以设置这个回调函数的标志值。或者利用这个结构体指针作为标识,从队列中找到这个回调函数并删除之。如下面代码所示:
- int
- evbuffer_cb_set_flags(struct evbuffer *buffer,
- struct evbuffer_cb_entry *cb, ev_uint32_t flags)
- {
-
- flags &= ~EVBUFFER_CB_INTERNAL_FLAGS;
- EVBUFFER_LOCK(buffer);
- cb->flags |= flags;
- EVBUFFER_UNLOCK(buffer);
- return 0;
- }
-
- int
- evbuffer_cb_clear_flags(struct evbuffer *buffer,
- struct evbuffer_cb_entry *cb, ev_uint32_t flags)
- {
-
- flags &= ~EVBUFFER_CB_INTERNAL_FLAGS;
- EVBUFFER_LOCK(buffer);
- cb->flags &= ~flags;
- EVBUFFER_UNLOCK(buffer);
- return 0;
- }
-
- int
- evbuffer_remove_cb_entry(struct evbuffer *buffer,
- struct evbuffer_cb_entry *ent)
- {
- EVBUFFER_LOCK(buffer);
- TAILQ_REMOVE(&buffer->callbacks, ent, next);
- EVBUFFER_UNLOCK(buffer);
- mm_free(ent);
- return 0;
- }
-
-
- int
- evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg)
- {
- struct evbuffer_cb_entry *cbent;
- int result = -1;
- EVBUFFER_LOCK(buffer);
- TAILQ_FOREACH(cbent, &buffer->callbacks, next) {
- if (cb == cbent->cb.cb_func && cbarg == cbent->cbarg) {
- result = evbuffer_remove_cb_entry(buffer, cbent);
- goto done;
- }
- }
- done:
- EVBUFFER_UNLOCK(buffer);
- return result;
- }
-
-
- static void
- evbuffer_remove_all_callbacks(struct evbuffer *buffer)
- {
- struct evbuffer_cb_entry *cbent;
-
- while ((cbent = TAILQ_FIRST(&buffer->callbacks))) {
- TAILQ_REMOVE(&buffer->callbacks, cbent, next);
- mm_free(cbent);
- }
- }
前面的代码展示了两个回调函数的类型,分别是evbuffer_cb_func和evbuffer_cb。后者在回调的时候可以得知删除或者添加数据之前的数据量和之后的数据量。但这两个数值都可以通过evbuffer_cb_info获取。所以evbuffer_cb回调类型的优势没有了。此外,还有一个问题。一般是通过evbuffer_setcb函数设置evbuffer_cb类型的回调函数。而这个函数会先删除之前添加的所有回调函数。
- void
- evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
- {
- EVBUFFER_LOCK(buffer);
-
- if (!TAILQ_EMPTY(&buffer->callbacks))
- evbuffer_remove_all_callbacks(buffer);
-
- if (cb) {
- struct evbuffer_cb_entry *ent =
- evbuffer_add_cb(buffer, NULL, cbarg);
- ent->cb.cb_obsolete = cb;
- ent->flags |= EVBUFFER_CB_OBSOLETE;
- }
- EVBUFFER_UNLOCK(buffer);
- }
可以看到,evbuffer_setcb为标志位flags加上了EVBUFFER_CB_OBSOLETE属性。从名字可以看到这是一个已经过时的属性。其实evbuffer_setcb已经被推荐使用了。
Libevent如何调用回调函数:
下面看一下是怎么调用回调函数的。其实现也简单,直接遍历回调队列,然后依次调用回调函数。
- static void
- evbuffer_run_callbacks(struct evbuffer *buffer, int running_deferred)
- {
- struct evbuffer_cb_entry *cbent, *next;
- struct evbuffer_cb_info info;
- size_t new_size;
- ev_uint32_t mask, masked_val;
- int clear = 1;
-
- if (running_deferred) {
- mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
- masked_val = EVBUFFER_CB_ENABLED;
- } else if (buffer->deferred_cbs) {
- mask = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
- masked_val = EVBUFFER_CB_NODEFER|EVBUFFER_CB_ENABLED;
-
-
- clear = 0;
- } else {
- mask = EVBUFFER_CB_ENABLED;
- masked_val = EVBUFFER_CB_ENABLED;
- }
-
- if (TAILQ_EMPTY(&buffer->callbacks)) {
-
- buffer->n_add_for_cb = buffer->n_del_for_cb = 0;
- return;
- }
-
-
- if (buffer->n_add_for_cb == 0 && buffer->n_del_for_cb == 0)
- return;
-
- new_size = buffer->total_len;
- info.orig_size = new_size + buffer->n_del_for_cb - buffer->n_add_for_cb;
- info.n_added = buffer->n_add_for_cb;
- info.n_deleted = buffer->n_del_for_cb;
- if (clear) {
- buffer->n_add_for_cb = 0;
- buffer->n_del_for_cb = 0;
- }
-
-
- for (cbent = TAILQ_FIRST(&buffer->callbacks);
- cbent != TAILQ_END(&buffer->callbacks);
- cbent = next) {
-
- next = TAILQ_NEXT(cbent, next);
-
-
- if ((cbent->flags & mask) != masked_val)
- continue;
-
- if ((cbent->flags & EVBUFFER_CB_OBSOLETE))
- cbent->cb.cb_obsolete(buffer,
- info.orig_size, new_size, cbent->cbarg);
- else
- cbent->cb.cb_func(buffer, &info, cbent->cbarg);
- }
- }
无论是删除数据还是添加数据的函数,例如evbuffer_add和evbuffer_drain函数,都是会调用evbuffer_invoke_callbacks函数的。而这个函数会调用evbuffer_run_callbacks函数。
evbuffer与网络IO:
从Socket中读取数据:
Libevent通过evbuffer_read函数从一个socket中读取数据到evbuffer中。在读取socket数据之前,Libevent会调用ioctl函数来获取这个socket的读缓冲区中有多少字节,进而确定本次要读多少字节到evbuffer中。Libevent会根据要读取的字节数,在真正read之前会先把evbuffer扩容,免得在read的时候缓冲区不够。
在扩容的时候,如果所在的系统是支持类似readv这样的可以把数据读取到像一个iovec结构体函数,那么Libevent就会选择在n个evbuffer_chain中找到足够的空闲空间(往往通过申请堆空间),因为这样可以使用类似Linux的iovec结构体。把链表的各个evbuffer_chain的空闲空间的地址赋值给iovec数组(如下图所示),然后调用readv函数直接读取,readv会把数据读取到相应的chain中。
上图中,有一点是和Libevent的实际情况不符合的。在evbuffer_read中,最后的那个几个evbuffer_chain一般是没有数据的,只有空闲的区域。上图为了好看,就加上了data这区域。
将链表的各个evbuffer_chain的空闲空间的地址赋值给iovec数组,这个操作是由函数_evbuffer_read_setup_vecs完成的。
-
-
-
- int
- _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
- struct evbuffer_iovec *vecs, int n_vecs_avail,
- struct evbuffer_chain ***chainp, int exact)
- {
- struct evbuffer_chain *chain;
- struct evbuffer_chain **firstchainp;
- size_t so_far;
- int i;
-
- if (howmuch < 0)
- return -1;
-
- so_far = 0;
-
-
- firstchainp = buf->last_with_datap;
- if (CHAIN_SPACE_LEN(*firstchainp) == 0) {
- firstchainp = &(*firstchainp)->next;
- }
-
-
-
-
- chain = *firstchainp;
- for (i = 0; i < n_vecs_avail && so_far < (size_t)howmuch; ++i) {
- size_t avail = (size_t) CHAIN_SPACE_LEN(chain);
-
-
- if (avail > (howmuch - so_far) && exact)
- avail = howmuch - so_far;
- vecs[i].iov_base = CHAIN_SPACE_PTR(chain);
- vecs[i].iov_len = avail;
- so_far += avail;
- chain = chain->next;
- }
-
- *chainp = firstchainp;
- return i;
- }
在Windows系统,虽然没有readv函数,但它有WSARecv函数,可以把数据读取到一个类似iovec的结构体中,所以在Windows系统中,Libevent还是选择在n个evbuffer_chain中找到足够的空闲空间。所以在Libevent中有下面的宏定义:
-
-
- #if defined(_EVENT_HAVE_SYS_UIO_H) || defined(WIN32)
- #define USE_IOVEC_IMPL //该宏标志所在的系统支持类似readv的函数
- #endif
-
-
- typedef struct __WSABUF {
- u_long len;
- char FAR *buf;
- } WSABUF, *LPWSABUF;
如果所在的系统不支持类似readv这样的函数,那么Libevent就只能在一个evbuffer_chain申请一个足够大的空间,然后直接调用read函数了。
前面说到的扩容,分别是由函数_evbuffer_expand_fast和函数evbuffer_expand_singlechain完成的。在《evbuffer结构与基本操作》一文中已经有对这两个函数的介绍,这里就不多说了。
由于存在是否支持类似readv函数 这两种情况,所以evbuffer_read在实现上也出现了两种实现。
上面说了这么多,还是来看一下evbuffer_read的具体实现吧。
-
-
- int
- evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
- {
- struct evbuffer_chain **chainp;
- int n;
- int result;
-
- #ifdef USE_IOVEC_IMPL //所在的系统支持iovec或者是Windows操作系统
- int nvecs, i, remaining;
- #else
- struct evbuffer_chain *chain;
- unsigned char *p;
- #endif
-
- EVBUFFER_LOCK(buf);
-
-
- if (buf->freeze_end) {
- result = -1;
- goto done;
- }
-
-
- n = get_n_bytes_readable_on_socket(fd);
- if (n <= 0 || n > EVBUFFER_MAX_READ)
- n = EVBUFFER_MAX_READ;
- if (howmuch < 0 || howmuch > n)
- howmuch = n;
-
- #ifdef USE_IOVEC_IMPL //所在的系统支持iovec或者是Windows操作系统
-
-
-
- if (_evbuffer_expand_fast(buf, howmuch, NUM_READ_IOVEC) == -1) {
- result = -1;
- goto done;
- } else {
-
- IOV_TYPE vecs[NUM_READ_IOVEC];
-
- #ifdef _EVBUFFER_IOVEC_IS_NATIVE //所在的系统支持iovec结构体
- nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
- NUM_READ_IOVEC, &chainp, 1);
- #else //Windows系统。因为没有native的 iovec
-
-
-
- struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
- nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,
- &chainp, 1);
-
-
-
- for (i=0; i < nvecs; ++i)
- WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
- #endif
-
-
- #ifdef WIN32
- {
- DWORD bytesRead;
- DWORD flags=0;
-
- if (WSARecv(fd, vecs, nvecs, &bytesRead, &flags, NULL, NULL)) {
- if (WSAGetLastError() == WSAECONNABORTED)
- n = 0;
- else
- n = -1;
- } else
- n = bytesRead;
- }
- #else
- n = readv(fd, vecs, nvecs);
- #endif
- }
-
-
-
- #else /*!USE_IOVEC_IMPL*/
-
-
- if ((chain = evbuffer_expand_singlechain(buf, howmuch)) == NULL) {
- result = -1;
- goto done;
- }
-
- p = chain->buffer + chain->misalign + chain->off;
-
-
- #ifndef WIN32
- n = read(fd, p, howmuch);
- #else
- n = recv(fd, p, howmuch, 0);
- #endif
-
- #endif /* USE_IOVEC_IMPL *///终止前面的if宏
-
-
- if (n == -1) {
- result = -1;
- goto done;
- }
- if (n == 0) {
- result = 0;
- goto done;
- }
-
-
- #ifdef USE_IOVEC_IMPL//使用了iovec结构体读取数据。需要做一些额外的处理
-
-
-
-
- remaining = n;
-
-
- for (i=0; i < nvecs; ++i) {
-
-
-
- ev_ssize_t space = (ev_ssize_t) CHAIN_SPACE_LEN(*chainp);
- if (space < remaining) {
- (*chainp)->off += space;
- remaining -= (int)space;
- } else {
- (*chainp)->off += remaining;
- buf->last_with_datap = chainp;
- break;
- }
- chainp = &(*chainp)->next;
- }
- #else
- chain->off += n;
-
- advance_last_with_data(buf);
- #endif
-
- buf->total_len += n;
- buf->n_add_for_cb += n;
-
-
- evbuffer_invoke_callbacks(buf);
- result = n;
- done:
- EVBUFFER_UNLOCK(buf);
- return result;
- }
往socket写入数据:
因为evbuffer是用链表的形式存放数据,所以要把这些链表上的数据写入socket,那么使用writev这个函数是十分有效的。同前面一样,使用iovec结构体数组,就需要设置数组元素的指针。这个工作由evbuffer_write_iovec函数完成。
正如前面的从socket读出数据,可能所在的系统并不支持writev这样的函数。此时就只能使用一般的write函数了,但这个函数要求数据放在一个连续的空间。所以Libevent有一个函数evbuffer_pullup,用来把链表内存拉直,即把一定数量的数据从链表中copy到一个连续的内存空间。这个连续的空间也是由某个evbuffer_chain的buffer指针指向,并且这个evbuffer_chain会被插入到链表中。这个时候就可以直接使用write或者send函数发送这特定数量的数据了。
不同于读,写操作还有第三种可能。那就是sendfile。如果所在的系统支持sendfile,并且用户是通过evbuffer_add_file添加数据的,那么此时Libevent就是所在系统的sendfile函数发送数据。
Libevent内部一般通过evbuffer_write函数把数据写入到socket fd中。下面是具体的实现。
-
- int
- evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd)
- {
-
- return evbuffer_write_atmost(buffer, fd, -1);
- }
-
-
- int
- evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd,
- ev_ssize_t howmuch)
- {
- int n = -1;
-
- EVBUFFER_LOCK(buffer);
-
-
- if (buffer->freeze_start) {
- goto done;
- }
-
- if (howmuch < 0 || (size_t)howmuch > buffer->total_len)
- howmuch = buffer->total_len;
-
- if (howmuch > 0) {
- #ifdef USE_SENDFILE //所在的系统支持sendfile
- struct evbuffer_chain *chain = buffer->first;
-
- if (chain != NULL && (chain->flags & EVBUFFER_SENDFILE))
- n = evbuffer_write_sendfile(buffer, fd, howmuch);
- else {
- #endif
- #ifdef USE_IOVEC_IMPL //所在的系统支持writev这类函数
-
- n = evbuffer_write_iovec(buffer, fd, howmuch);
- #elif defined(WIN32)
-
-
-
-
-
- void *p = evbuffer_pullup(buffer, howmuch);
- n = send(fd, p, howmuch, 0);
- #else
- void *p = evbuffer_pullup(buffer, howmuch);
- n = write(fd, p, howmuch);
- #endif
- #ifdef USE_SENDFILE
- }
- #endif
- }
-
- if (n > 0)
- evbuffer_drain(buffer, n);
-
- done:
- EVBUFFER_UNLOCK(buffer);
- return (n);
- }
参考:
http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html