libevent学习---使用evbuffer操作数据

(一)向输出缓冲添加数据

int bufferevent_write(struct bufferevent *bufev,
    const void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);

通过这两个函数,可以向bufferevent的输出缓冲区添加数据。
bufferevent_write_bufferremovebuf中的所有数据,并将其添加到输出缓冲区末尾

(二)从输入缓冲区读入数据

例如服务端从网络套接子读入数据时,提供了如下两个接口:

size_t bufferevent_read(struct bufferevent *bufev,void *data,size_t size);
int bufferevent_read_buffer(struct bufferevent*bufev,struct evbuffer *buf);

同样的道理,只不过方向相反,是将bufferevent中的数据移除。

这样就需要保证data指针所指向的空间有足够的空间容纳size大小的数据。

(三)示例

完整代码可以参考gayhub
比如在一个简单的echo服务中可以这么写:

void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE    256
    char line[MAX_LINE+1];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);

    while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
    line[n] = '\0';
    printf("fd=%u, read line: %s\n", fd, line);

    bufferevent_write(bev, line, n);
    }
}

(四)使用evbuffer

在这之前,可以先简单看一下evbufferbufferevent的关系

每一个bufferevent中有一个input bufferoutput buffer来看这个结构体:

struct bufferevent {
///....
    /** An input buffer. Only the bufferevent is allowed to add data to
        this buffer, though the user is allowed to drain it. */
    struct evbuffer *input;

    /** An input buffer. Only the bufferevent is allowed to drain data
        from this buffer, though the user is allowed to add it. */
    struct evbuffer *output;
///....
};

当我们使用bufferevent来写数据的时候,我们实际是将数据添加到了output中,
当我们读取bufferevent的数据时,我们实际从input读入。

再看bufferevent的接口,实际上是对evbuffer操作的封装,例如:

size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
    return (evbuffer_remove(bufev->input, data, size));
}

int
bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf)
{
    return (evbuffer_add_buffer(buf, bufev->input));
}

在初始化bufferevent的时候,会给evbuffer申请空间:

struct evbuffer *
evbuffer_new(void)
{
    struct evbuffer *buffer;

    //底层是封装calloc
    buffer = mm_calloc(1, sizeof(struct evbuffer));//申请空间
    if (buffer == NULL)
        return (NULL);
    TAILQ_INIT(&buffer->callbacks);
    buffer->refcnt = 1;
    buffer->last_with_datap = &buffer->first;

    return (buffer);
}

搞清楚evbufferbufferevent的关系后,我们可以使用一些更加灵活的接口:

struct evbuffer *bufferevent_get_input(struct bufferevent*bufev);
struct evbuffer *bufferevent_get_output(struct bufferevent*bufev);

直接获取evbuffer的指针,源码中很简单的一个return

struct evbuffer *
bufferevent_get_input(struct bufferevent *bufev)
{
    return bufev->input;
}

1.添加数据到evbuffer

int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen);
int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)

//例如:
evbuffer_add(buf,"Hello world",11);
evbuffer_add_printf(buf,"Hello %s","zhangxiao");

2.evbuffer之间的数据移动

int evbuffer_add_buffer(struct evbuffer*dst,struct evbuffer*src);
int evbuffer_remove_buffer(struct evbuffer *src,struct evbuffer*dst,size_t datlen);

虽然没有看过源码,这部分,根据测试得知,两个函数都实现了类似C++中的移动语义,完成操作后src中的数据被转移到dst

3.在prepend添加数据*

所谓的头插法,因为底层是以链表的形式构造buffer块,所以这种方式很容易实现:

int evbuffer_prepend(struct evbuffer*buf, const void *data,size_t size);
int evbuffer_prepend_buffer(struct evbuffer *dst,struct evbuffer *src);

比如说,在echo中可以用prepend加一些前缀:

//evbuffer_new返回了新的empty evbuffer
//evbuffer_free删除
void read_cb(struct bufferevent *bev, void *arg)
{
    struct evbuffer *input;
    struct evbuffer *output;
    output=bufferevent_get_output(bev);
    input=bufferevent_get_input(bev);
    printf("read %d data\r\n",evbuffer_get_length(input));
    struct evbuffer * tmp = evbuffer_new();

    const char t[]=">> ";
    evbuffer_add_buffer(tmp,input);
    evbuffer_prepend(tmp,t,strlen(t));
    //evbuffer_remove_buffer(input,output,evbuffer_get_length(input));
    //
    evbuffer_add_buffer(output,tmp);
    evbuffer_free(tmp);
}

4.pullup数据

不知道怎么翻译,总之就是可以把evbuffer中的数据拿出来,由于在底层以链表的形式构造,这个函数可以线性化前size字节的数据。

unsigned char * evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size);
//当size<0 将返回所有数据
//

unsigned char * p = evbuffer_pullup(tmp,evbuffer_get_length(tmp));
printf("%s\r\n",p);

5.回收数据

libevent对于这种行为用了一个词叫”drain”,很贴切,这种行为就类似discard,在muduo中有retrieve,意思差不多吧。

//drain直接丢弃len长度的数据
//remove将数据拷贝到data中
int evbuffer_drain(struct evbuffer *buf, size_t len);
int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen);

6.按行读取数据

http协议中每个请求头都用\r\n也就是CRLF来隔开,evbuffer提供了方便的接口来操作:

struct evbuffer*buf=bufferevent_get_input(buf);
request_line = evbuffer_readln(buf,&len,EVBUFFER_EOL_CRLF);

evbuffer的接口还有很多,这里就不一一列举了,接下来,该学习相关源码了。

(五)参考

1.blog.csdn.net/yusiguyuan
2.libevent深入浅出
3.libevent programming

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值