libevent的bufferevent剖析

1.什么是bufferevent

libevent为输入输出缓冲区做出了一个抽象,这个抽象就是bufferevent。用来自动管理输入输出缓冲区,我们要输入输出就可以对bufferevent操作了。有数据写入bufferevent时,添加数据到输出缓冲区。bufferevent有数据供读取的时候,从输入缓冲区抽取数据。

2.bufferevent结构体

struct bufferevent {
	struct event_base *ev_base;   //关联的event_base

	struct event ev_read;  //读事件
	struct event ev_write;  //写事件

	struct evbuffer *input;    //输入buffer
	struct evbuffer *output;  //输出buffer

	struct event_watermark wm_read;  //读水位
	struct event_watermark wm_write; //写水位

	evbuffercb readcb;   //buffer读回调函数
	evbuffercb writecb;  //buffer写回调函数
	everrorcb errorcb;    //错误回调函数
	void *cbarg;        //回调函数的参数

	int timeout_read;	/* in seconds */
	int timeout_write;	/* in seconds */

	short enabled;	/* events that are currently enabled */
};
3.关于回调函数和水位

bufferevent一共有三个回调,两个用于读写数据。

readcb:读取任意量的数据后都会触发读取回调,默认0

writecb:数据被消耗一定数量后会触发写入回调,默认0

那么水位是什么呢?

一共有四个水位,读取低水位,读取高水位,写入低水位,写入高水位。程序注释中我们就可以看到这几个的作用了。

  读取低水位:读取操作使得输入缓冲区的数据量在此级别或者更高时,读取回调将被调用。默认值为0,所以每个读取操作都会导致读取回调被调用。

   读取高水位:输入缓冲区中的数据量达到此级别后,bufferevent将停止读取,直到输入缓冲区中足够量的数据被抽取,使得数据量低于此级别。默认值是无限,所以永远不会因为输入缓冲区的大小而停止读取。

  写入低水位:写入操作使得输出缓冲区的数据量达到或者低于此级别时,写入回调将被调用。默认值是0,所以只有输出缓冲区空的时候才会调用写入回调。

  写入高水位:bufferevent没有直接使用这个水位。它在bufferevent用作另外一个bufferevent的底层传输端口时有特殊意义。请看后面关于过滤型

bufferevent也有“错误”或者“事件”回调,用于向应用通知非面向数据的事件,如连接已经关闭或者发生错误:

比如我们关注了EV_TIMEOUT属性,在read_cb和write_cb函数中就会直接goto error,调用error回调函数。


下面是bufferevent的代码及注释:

void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);

static int
bufferevent_add(struct event *ev, int timeout)
{
	struct timeval tv, *ptv = NULL;

	if (timeout) {
		evutil_timerclear(&tv); //时间清空
		tv.tv_sec = timeout;
		ptv = &tv;
	}

	return (event_add(ev, ptv));  //加入反应堆相应队列,如果没有设置timeout,不会注册timeout事件,返回0
}

/* 
 * This callback is executed when the size of the input buffer changes.
 * We use it to apply back pressure on the reading side.
 */        //apply back means 申请回来

void
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,  //加read压力
    void *arg) {
	struct bufferevent *bufev = arg;
	/* 
	 * If we are below the watermark then reschedule reading if it's
	 * still enabled.
	 */
	if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {  //如果高水位线为0,或者当前小于高水位线,不回调
		evbuffer_setcb(buf, NULL, NULL); //为什么要调用这个函数?

		if (bufev->enabled & EV_READ)
			bufferevent_add(&bufev->ev_read, bufev->timeout_read);  //让这个时间过一定时间再读
	}										//timeout_read在bufferevent_settimeout函数里面会设置,
}

static void
bufferevent_readcb(int fd, short event, void *arg)  //缓冲区可读会触发该回调
{
	struct bufferevent *bufev = arg;
	int res = 0;
	short what = EVBUFFER_READ;
	size_t len;
	int howmuch = -1;

	/* Note that we only check for event==EV_TIMEOUT. If
	* event==EV_TIMEOUT|EV_READ, we can safely ignore the
	* timeout, since a read has occurred */
	if (event == EV_TIMEOUT) {     //如果是超时事件,忽略
		what |= EVBUFFER_TIMEOUT;
		goto error;
	}

	/*
	 * If we have a high watermark configured then we don't want to   // 是否设置了输入缓冲区的最大大小
	 * read more data than would make us reach the watermark.
	 */                              //先检测input缓冲区已有的数据
	if (bufev->wm_read.high != 0) {   //如果读取高水位不为0
		howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input);   //高水位长度-实际数据长度
		/* we might have lowered the watermark, stop reading */ //我们可能已经降低了水位,停止阅读,水位改动
		if (howmuch <= 0) {   //如果为<=0,说明输入缓冲区的数据量达到了一定级别,bufferevent停止读取
			struct evbuffer *buf = bufev->input;
			event_del(&bufev->ev_read);   //不能读了,删除可读事件
			evbuffer_setcb(buf,
			    bufferevent_read_pressure_cb, bufev);   
			return;  //直接返回,不再读取
		}
	}

	res = evbuffer_read(bufev->input, fd, howmuch);   //从fd读取数据到bufev->input
	if (res == -1) {
		if (errno == EAGAIN || errno == EINTR)   //EAGAIN字面意思是再试一次,比如打开以nonblock打开一个socket?
										//连续read但无数据可读,就会返回EAGAIN,意思让你再试一次。EINTR
			goto reschedule;    //跳到重新计划
		/* error case */
		what |= EVBUFFER_ERROR;    //否则加上EVBUFFER_ERROR
	} else if (res == 0) {
		/* eof case */
		what |= EVBUFFER_EOF;  //缓冲区结尾了
	}

	if (res <= 0)
		goto error;

	bufferevent_add(&bufev->ev_read, bufev->timeout_read); //添加超时

	/* See if this callbacks meets the water marks */
	len = EVBUFFER_LENGTH(bufev->input);
	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)   //如果最低水位线不等于0,且实际字节小于最低水位线,不处理,继续读取fd
		return;
	if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) {//最高水位线不为0,且长度大于最高水位线  //这是从fd读取数据之后的情况
		struct evbuffer *buf = bufev->input;  
		event_del(&bufev->ev_read);      //从注册链表中删除

		/* Now schedule a callback for us when the buffer changes */
		evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);   //设置读压回调函数
	}

	/* Invoke the user callback - must always be called last */
	if (bufev->readcb != NULL)
		(*bufev->readcb)(bufev, bufev->cbarg);  
	return;

 reschedule:
	bufferevent_add(&bufev->ev_read, bufev->timeout_read);   //一定时间后再读
	return;

 error:
	(*bufev->errorcb)(bufev, what, bufev->cbarg);
}

static void
bufferevent_writecb(int fd, short event, void *arg)
{
	struct bufferevent *bufev = arg;
	int res = 0;
	short what = EVBUFFER_WRITE;

	if (event == EV_TIMEOUT) {
		what |= EVBUFFER_TIMEOUT;   //同样ignore
		goto error;
	}

	if (EVBUFFER_LENGTH(bufev->output)) {  //如果有数据
	    res = evbuffer_write(bufev->output, fd);  //有数据就直接写到fd
	    if (res == -1) {
#ifndef WIN32
/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
 *set errno. thus this error checking is not portable*/
		    if (errno == EAGAIN ||
			errno == EINTR ||
			errno == EINPROGRESS)
			    goto reschedule;
		    /* error case */
		    what |= EVBUFFER_ERROR;

#else
				goto reschedule;
#endif

	    } else if (res == 0) {
		    /* eof case */
		    what |= EVBUFFER_EOF;
	    }
	    if (res <= 0)
		    goto error;
	}

	if (EVBUFFER_LENGTH(bufev->output) != 0)  //如果输出缓冲区还有数据,那就过段时间再输出
		bufferevent_add(&bufev->ev_write, bufev->timeout_write);

	/*
	 * Invoke the user callback if our buffer is drained or below the
	 * low watermark.
	 */
	if (bufev->writecb != NULL &&
	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)//到达低水位的回调
		(*bufev->writecb)(bufev, bufev->cbarg);

	return;

 reschedule:
	if (EVBUFFER_LENGTH(bufev->output) != 0)  //如果数据不为0,一段时间后重写
		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
	return;

 error:
	(*bufev->errorcb)(bufev, what, bufev->cbarg);
}

/*
 * Create a new buffered event object.
 *
 * The read callback is invoked whenever we read new data.
 * The write callback is invoked whenever the output buffer is drained.
 * The error callback is invoked on a write/read error or on EOF.
 *
 * Both read and write callbacks maybe NULL.  The error callback is not
 * allowed to be NULL and have to be provided always.
 */

//分配bufferevent结构体
struct bufferevent *
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
    everrorcb errorcb, void *cbarg)
{
	struct bufferevent *bufev;

	if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
		return (NULL);

	if ((bufev->input = evbuffer_new()) == NULL) {   //分配evbuffer
		free(bufev);
		return (NULL);
	}

	if ((bufev->output = evbuffer_new()) == NULL) {
		evbuffer_free(bufev->input);   //如果失败,析构掉之前的,避免内存泄漏
		free(bufev);
		return (NULL);
	}

	event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);  //设置bufferevent内部读写事件
	event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);

	bufferevent_setcb(bufev, readcb, writecb, errorcb, cbarg);

	/*
	 * Set to EV_WRITE so that using bufferevent_write is going to
	 * trigger a callback.  Reading needs to be explicitly enabled
	 * because otherwise no data will be available.
	 */                              //自动开启EV_WRITE,EV_READ需要显示声明
	bufev->enabled = EV_WRITE; // /** Events that are currently enabled: currently EV_READ and EV_WRITE are supported. */

	return (bufev);
}

void
bufferevent_setcb(struct bufferevent *bufev,
    evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg)
{
	bufev->readcb = readcb;
	bufev->writecb = writecb;
	bufev->errorcb = errorcb;

	bufev->cbarg = cbarg;
}

void
bufferevent_setfd(struct bufferevent *bufev, int fd)   //
{
	event_del(&bufev->ev_read);   //先解注册之前的事件
	event_del(&bufev->ev_write);

	event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
	event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
	if (bufev->ev_base != NULL) {
		event_base_set(bufev->ev_base, &bufev->ev_read);
		event_base_set(bufev->ev_base, &bufev->ev_write);
	}

	/* might have to manually trigger event registration */
}

int
bufferevent_priority_set(struct bufferevent *bufev, int priority)
{
	if (event_priority_set(&bufev->ev_read, priority) == -1)   //设置优先级
		return (-1);
	if (event_priority_set(&bufev->ev_write, priority) == -1)
		return (-1);

	return (0);
}

/* Closing the file descriptor is the responsibility of the caller */

void
bufferevent_free(struct bufferevent *bufev)
{
	event_del(&bufev->ev_read);
	event_del(&bufev->ev_write);

	evbuffer_free(bufev->input);
	evbuffer_free(bufev->output);

	free(bufev);
}

/*
 * Returns 0 on success;
 *        -1 on failure.
 */

//bufferevent写事件
int
bufferevent_write(struct bufferevent *bufev, const void *data, size_t size)
{
	int res;

	res = evbuffer_add(bufev->output, data, size);   //调用追加data函数,即evbuffer_add函数

	if (res == -1)
		return (res);

	/* If everything is okay, we need to schedule a write */
	if (size > 0 && (bufev->enabled & EV_WRITE))     //上面data追加完毕,现在将bufev->ev_write事件加入反应堆,等候
		bufferevent_add(&bufev->ev_write, bufev->timeout_write);

	return (res);
}

int
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
{
	int res;

	res = bufferevent_write(bufev, buf->buffer, buf->off);
	if (res != -1)
		evbuffer_drain(buf, buf->off);  //为什么发送失败要drain

	return (res);
}

size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
	struct evbuffer *buf = bufev->input;

	if (buf->off < size)     //如果小于要读的字节数
		size = buf->off;    //就读实际数据

	/* Copy the available data to the user buffer */
	memcpy(data, buf->buffer, size);

	if (size)
		evbuffer_drain(buf, size); //读完size字节后调整一下

	return (size);
}

//将某个event添加到event_base中,bufferevent开关
int
bufferevent_enable(struct bufferevent *bufev, short event)
{
	if (event & EV_READ) {
		if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
			return (-1);
	}
	if (event & EV_WRITE) {
		if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
			return (-1);
	}

	bufev->enabled |= event;
	return (0);
}

//从对应队列删除
int
bufferevent_disable(struct bufferevent *bufev, short event)
{
	if (event & EV_READ) {
		if (event_del(&bufev->ev_read) == -1)
			return (-1);
	}
	if (event & EV_WRITE) {
		if (event_del(&bufev->ev_write) == -1)
			return (-1);
	}

	bufev->enabled &= ~event;
	return (0);
}

/*
 * Sets the read and write timeout for a buffered event.
 */

//设置超时
void
bufferevent_settimeout(struct bufferevent *bufev,
    int timeout_read, int timeout_write) {
	bufev->timeout_read = timeout_read;
	bufev->timeout_write = timeout_write;

	if (event_pending(&bufev->ev_read, EV_READ, NULL))//检测时间是否处于未决状态
		bufferevent_add(&bufev->ev_read, timeout_read);
	if (event_pending(&bufev->ev_write, EV_WRITE, NULL))
		bufferevent_add(&bufev->ev_write, timeout_write);
}  

/*
 * Sets the water marks
 */
//设置水位线啊
void
bufferevent_setwatermark(struct bufferevent *bufev, short events,
    size_t lowmark, size_t highmark)
{
	if (events & EV_READ) {
		bufev->wm_read.low = lowmark;
		bufev->wm_read.high = highmark;
	}

	if (events & EV_WRITE) {
		bufev->wm_write.low = lowmark;
		bufev->wm_write.high = highmark;
	}

	/* If the watermarks changed then see if we should call read again */
	bufferevent_read_pressure_cb(bufev->input,
	    0, EVBUFFER_LENGTH(bufev->input), bufev);
}
//和event_base关联
int
bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
{
	int res;

	bufev->ev_base = base;

	res = event_base_set(base, &bufev->ev_read);
	if (res == -1)
		return (res);

	res = event_base_set(base, &bufev->ev_write);
	return (res);
}

下面上一个测试例子:

include <iostream>
#include <event.h>
#include <assert.h>
#include <socket.h>
using namespace std;

struct event_base* base = NULL;
struct bufferevent* buf_ev = NULL;

void read_cb(struct bufferevent* buf, void* arg)
{
///read thing from bufferevent///
    cout<<"read"<<endl;
    char data[1024] = {0};
    assert(buf_ev->input->off >= 0); 
    cout<<buf_ev->input->off<<endl;
    bufferevent_read(buf_ev, data, sizeof(data));
    assert(data != NULL);
    cout<<data;
/
    int connfd = *(int *)arg;
    bufferevent_write(buf_ev, data, strlen(data)+1);
}

void write_cb(struct bufferevent* buf, void* arg)
{
    cout<<"write"<<endl;
}

void error_cb(struct bufferevent* buf, short what, void* arg)
{
    cout<<"error"<<endl;
///read thing from bufferevent///
    char data[1024] = {0};
    assert(buf_ev->input->off > 0); 
    cout<<buf_ev->input->off<<endl;
    bufferevent_read(buf_ev, data, sizeof(data));
    assert(data != NULL);
    cout<<data;
/
    int connfd = *(int *)arg;
    bufferevent_write(buf_ev, data, strlen(data)+1);
}

int main()
{
    base = event_init();
    assert(base != NULL);

    int sockfd = socket_create("127.0.0.1", 6666);
    assert(sockfd != -1);

    int connfd = accept(sockfd, NULL, NULL);
    assert(connfd != -1);

    buf_ev = bufferevent_new(connfd, read_cb, write_cb, error_cb, &connfd);
    assert(buf_ev != NULL);

    int ret;
    ret = bufferevent_base_set(base, buf_ev);
    assert(ret != -1);

    ret = bufferevent_enable(buf_ev, EV_READ);
    assert(ret != -1);


    //ret = bufferevent_disable(buf_ev, EV_READ);       //if the bufferevent was disabled, ex: shut the EV_READ, reading will not excute.
    //assert(ret != -1);
///
    bufferevent_setwatermark(buf_ev, EV_READ, 3, 10);

    bufferevent_settimeout(buf_ev, 4, 100);


    event_base_dispatch(base);

    return 0;
}
(FreeeLinux的博客:http://blog.csdn.net/freeelinux/article/details/52862170)

libeventbufferevent 可以用于收发数据,具体步骤如下: 1. 创建 bufferevent 对象: ``` struct bufferevent *bev; bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); ``` 其中,base 是 event_base 对象,fd 是已连接的 socket 文件描述符,BEV_OPT_CLOSE_ON_FREE 表示在释放 bufferevent 对象时关闭底层的 socket 连接。 2. 设置回调函数: ``` void bev_read_cb(struct bufferevent *bev, void *ctx) { // 处理读事件 } void bev_write_cb(struct bufferevent *bev, void *ctx) { // 处理写事件 } void bev_event_cb(struct bufferevent *bev, short events, void *ctx) { // 处理事件 } bufferevent_setcb(bev, bev_read_cb, bev_write_cb, bev_event_cb, NULL); ``` bufferevent_setcb 函数用于设置 bufferevent 对象的回调函数,包括读、写、事件回调函数。 3. 启用 bufferevent: ``` bufferevent_enable(bev, EV_READ|EV_WRITE); ``` bufferevent_enable 函数用于启用 bufferevent 对象的读写事件。 4. 发送数据: ``` const char *data = "hello, world"; bufferevent_write(bev, data, strlen(data)); ``` bufferevent_write 函数用于向 bufferevent 对象写入数据。 5. 接收数据: ``` void bev_read_cb(struct bufferevent *bev, void *ctx) { char buf[1024]; size_t len = bufferevent_read(bev, buf, sizeof(buf)); // 处理接收到的数据 } ``` bev_read_cb 回调函数中,使用 bufferevent_read 函数从 bufferevent 对象中读取数据。 以上就是 libeventbufferevent 收发数据的基本步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值