Chinaunix首页 | 论坛 | 博客
  • 博客访问: 281763
  • 博文数量: 46
  • 博客积分: 2021
  • 博客等级: 大尉
  • 技术积分: 406
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-03 13:00
文章分类

全部博文(46)

文章存档

2011年(1)

2010年(9)

2009年(2)

2007年(13)

2006年(21)

我的朋友

分类:

2010-04-02 10:31:25

Libeventevbuffer缓冲区分析

 

    在开发网络程序的时候,特别是TCP基于字节流的数据,需要从字节流数据中解析出自己的通讯协议,比如读一行数据:我们每次调用read函数的时候指定了我们期望读多少数据,但这个数据并不一定正好能读到‘\n’,这个时候就需要借助一个缓冲区来保存多余的数据,以便于和下一次读到的数据合并在一起继续分析。Evbuffer就是提供了这样一个缓冲区。

 

关于libevent的缓冲模块,主要就是围绕evbuffer结构体展开。先看下evbuffer的定义:

struct evbuffer{
  // 当前有效缓冲区的内存起始地址
 u_char *buffer;
  // 整个分配(realloc)用来缓冲的内存起始地址
  u_char *orig_buffer;
  // origin_buffer和buffer之间的字节数
 size_t misalign;
  // 整个分配用来缓冲的内存字节数
 size_t totallen;
  // 当前有效缓冲区的长度(字节数)
 size_t off;
  //回到函数,当缓冲区有变化的时候会被调用
 void (*cb)(struct evbuffer *, size_t, size_t, void *);
  //回调函数的参数
 void *cbarg;
};


下面简单介绍一下evbuffer提供的 API,我所使用的libevent版本是1.4.10-stable:

struct evbuffer* evbuffer_new(void)

动态分配一个struct evbuffer结构,需要调用evbuffer_free释放内存。


void evbuffer_free(struct evbuffer *buffer)

释放buffer所占用的内存。


int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)

data追加到evbuffer

先判断缓冲区的大小是否可以容纳的下datlen大小, 如果不能,则使用evbuffer_expand扩充容量。然后将data追加到evbuffer->buffer + evbuffer->off后。 并且更新有效缓冲区长度off

如果datlen > 0, 并且设置了回调函数,则调用回调函数
返回值:成功返回0,失败返回-1。


int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)

移动数据从一个evbuffer到另一个evbuffer

实际上还是调用了evbuffer_add添加数据到outbuf中。但会清除inbuf中的数据。

返回值:成功返回0, 失败返回-1。


int evbuffer_add_printf( struct evbuffer *, const char* fmt, ...)

添加一个格式化的字符串到evbuffer尾部。


int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)

添加一个va_list格式的字符串到evbuffer尾部。

void evbuffer_drain(struct evbuffer *buf, size_t len)

evbuffer起始位置删除指定长度len字节数据

如果len的长度大于等于缓冲区的off的长度,则表明缓冲区的数据都被清空。 

如果缓冲区发生变化,并且设置了回调函数,则调用回调函数。


int evbuffer_expand(struct evbuffer *buf, size_t datlen)

该函数用于扩充evbuffer的容量。每次向evbuffer写数据时,都是将数据写到buffer+off后,bufferbuffer+off之间已被使用,保存的是有效数据,而orig_bufferbuffer之间则是因为读取数据移动指针而形成的无效区域。

 

evbuffer_expand的扩充策略在于:

1,计算出加上datlen后需要的缓冲区大小need

2, 判断当前缓冲区的长度是否可以容纳的下need大小,如果可以则不需要改变缓冲区的大小,直接返回。

3,如果当前缓冲哦你去的长度容纳不下need大小,则判断orig_bufferbuffer之间的空闲区域是否可以容纳添加的数据,如果

可以,则移动bufferbuffer+off之间的数据到orig_bufferorig_buffer+off之间,然后把新的数据拷贝到orig_buffer+off之后;

4,如果misalign不可以容纳,那么重新分配更大的空间(realloc),同样会移动数据。

 

扩充内存的策略为:确保新的内存区域最小尺寸为256,且以乘以2的方式逐步扩大(2565121024...)

返回值:成功返回0, 失败返回-1


u_char *evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)

查找缓冲区中是否存在指定的字符串what

注意这里使用的是u_char类型,说明有可能查找的数据不是以’\0’结尾

如果存在返回指向字符串what的指针,没有则返回NULL


int evbuffer_read(struct evbuffer *buf, int fd, int howmuch)

调用read/recv函数,从文件描述符fd上读取数据到evbuffer中。如果缓冲区不够,调用evbuffer_expand扩充缓冲区。

int evbuffer_write(struct evbuffer *buffer, int fd)

把缓冲区中的数据,调用send/write函数写入文件描述符fd上, 如果send/write函数写入的字节数大于0,则调用evbuffer_drain删除已写的数据。

char *evbuffer_readline(struct evbuffer *buffer)

读取数据以"\r\n","\n\r", "\r" 或者 "\n"结尾。

返回动态分配内存,需要调用者自己使用free来释放内存。返回一个以\0结尾的字符串。

int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)

evbuffer缓冲区中的数据读到data中, 最多读datlen字节。如果缓冲区里的数据小于datlen,则拷贝缓冲区中全部数据。

然后调用evbuffer_drain删除已读数据。


void evbuffer_setcb(struct evbuffer *buffer,
    void (*cb)(struct evbuffer *, size_t, size_t, void *),
    void *cbarg)

设置回调函数。当缓冲区中发生变化时, 调用设置的回调函数。


Evbuffer提供的API已经全部介绍完毕,接下来我们通过一个实例进一步学习如何使用evbuffer, 想要使用evbuffer,系统里必须已经安装了libevent

例子代码如下:evbuffer-test.c


      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <assert.h>
      4
      5 //引入libevent头文件
      6 #include "event.h"
      7
      8 int main(int argc, char** argv)
      9 {
     10     struct evbuffer* buff = NULL;
     11     char c, c2[3] = {0};
     12
     13     buff = evbuffer_new();
     14     assert(buff != NULL);
     15
     16     evbuffer_add(buff, "1", 1);
     17     evbuffer_add(buff, "2", 1);
     18     evbuffer_add(buff, "3", 1);
     19     evbuffer_add_printf(buff, "%d%d", 4, 5);
     20     assert(buff->off == 5);
     21
     22     evbuffer_remove(buff, &c, sizeof(char));
     23     assert(c == '1');
     24     evbuffer_remove(buff, &c, sizeof(char));
     25     assert(c == '2');
     26     evbuffer_remove(buff, &c, sizeof(char));
     27     assert(c == '3');
     28     evbuffer_remove(buff, c2, 2);
     29     assert(strcmp(c2, "45") == 0);
     30
     31     assert(buff->off == 0);
     32
     33     evbuffer_add(buff, "test\r\n", 6);
     34     assert(buff->off == 6);
     35
     36     char* line = evbuffer_readline(buff);
     37     assert(strcmp(line, "test") ==0);
     38     assert(buff->off == 0);
     39     free(line);
     40
     41     evbuffer_free(buff);
     42
     43     printf("ok\n");
     44
     45     return 0;
     46 }


其中使用assert断言来测试是否达到我们的预期,如果assert失败,程序会被中断,并且输出类似的错误信息:

[zhanghua@localhost test]$ ./evbuffer-test evbuffer-test: evbuffer-test.c:31: main: Assertion `buff->off == 2' failed. Aborted (core dumped)


如果程序所有assert 全部达到我们的预期,程序会输出”OK”

我们使用如下指令编译程序:

gcc -I/home/zhanghua/project/athena/third_party/Linux/2.6-kernal/libevent-1.4.10-stable/include/ evbuffer-test.c -o evbuffer-test -L /home/zhanghua/project/athena/third_party/Linux/2.6-kernal/libevent-1.4.10-stable/lib –levent


其中需要说明的是 –I –L后面的路径是libevent 头文件和动态库所在的路径,这个需要和自己实际工作环境做调整。

-levent 是我们需要链接libevent 的动态库。

执行程序:

[zhanghua@localhost test]$ ./evbuffer-test 

 ok


    到这里evbuffer就简单的介绍完了,已经感觉到evbuffer的实用和强大了吧,在这里感谢开源者们提供众多优秀的代码供我们学习和使用。


阅读(4616) | 评论(0) | 转发(0) |
0

上一篇:jabberd-2源代码分析笔记

下一篇:libevent

给主人留下些什么吧!~~