libevent源码学习(19):缓冲区抽象bufferevent

目录

引言

bufferevent结构

bufferevent_private结构体

bufferevent结构体

创建一个基于socket的bufferevent

启动bufferevent

EVUTIL_UPCAST宏函数

bufferevent水位设置

bufferevent从fd中读取数据

bufferevent向fd中写入数据

从bufferevent中读出数据

向bufferevent中写入数据

bufferevent的回调机制

设置bufferevent的回调函数

为bufferevent的socket建立连接


以下源码均基于libevent-2.0.21-stable。

引言

       在前文中说到,evbuffer结构是用来描述一个缓冲区的,而对于每一个fd来说,都对应于两个缓冲区——一个用于读,一个则用于写。而对于每一个fd来说,要想对其缓冲区描述清楚,仅仅通过一个evbuffer结构是完全不够的,因此,bufferevent就从fd的层面对缓冲区进行了抽象并封装,用户只需操作fd相应的bufferevent即可完成对fd的读写了。

       从另一个方面来说,如果直接对fd进行读写,那么这种方式属于“不带缓冲”的读写fd;而如果对fd相应的bufferevent进行读写,那么这种方式就属于“带读写缓冲”的读写fd。

bufferevent结构

       bufferevent的结构比较特殊,它和event_base不一样:event_base的定义放在event_internal.h中,并不对用户开放,对用户开放的event.h中只有event_base的声明,因此,用户是无法去访问event_base的任何成员的,也不能用event_base myeb;这样的方式去创建一个event_base栈实例或者堆实例,要想创建就只能通过调用event_base_new这样函数去创建;而在bufferevent中则不一样了,可以看到在对用户开放的bufferevent_struct.h文件中就定义了bufferevent的结构,这样就使得用户可以直接去访问bufferevent中的这些变量,也可以直接创建一个bufferevent的栈实例或者堆实例,如下所示:

       那么为什么bufferevent没有像event_base那样禁止用户去创建实例或访问成员呢?在bufferevent结构体前面有这样一段话:

       也就是说,bufferevent从原则上来说是不应该把这些信息暴露给用户的,但是为了兼容之前版本的libevent,才将bufferevent的信息暴露出来。不过bufferevent也做了一些工作来避免用户访问一部分信息,这就是bufferevent_private结构体:

bufferevent_private结构体

/** Parts of the bufferevent structure that are shared among all bufferevent
 * types, but not exposed in bufferevent_struct.h. */
struct bufferevent_private {
	/** The underlying bufferevent structure. */
	struct bufferevent bev;  //bufferevent_private对应的bufferevent

	/** Evbuffer callback to enforce watermarks on input. */
	struct evbuffer_cb_entry *read_watermarks_cb;   //输入缓冲区达到高水位回调函数

	/** If set, we should free the lock when we free the bufferevent. */
	unsigned own_lock : 1;   

	/** Flag: set if we have deferred callbacks and a read callback is
	 * pending. */
	unsigned readcb_pending : 1;   //为1表示bufferevent的readcb被延迟回调
	/** Flag: set if we have deferred callbacks and a write callback is
	 * pending. */
	unsigned writecb_pending : 1;  //为1表示bufferevent的writecb被延迟回调
	/** Flag: set if we are currently busy connecting. */
	unsigned connecting : 1; //置为1说明connect成功连接或者需要重试
	/** Flag: set if a connect failed prematurely; this is a hack for
	 * getting around the bufferevent abstraction. */
	unsigned connection_refused : 1;  //置为1说明发生了connect发生了ECONNREFUSED错误
	/** Set to the events pending if we have deferred callbacks and
	 * an events callback is pending. */
	short eventcb_pending; //如果被设置,表示bufferevent的errorcb被延迟回调,eventcb_pending可能是多种事件的合成
	//可为BEV_EVENT_READING、BEV_EVENT_WRITING、BEV_EVENT_EOF、
	//BEV_EVENT_ERROR、BEV_EVENT_TIMEOUT、BEV_EVENT_CONNECTED
	
	/** If set, read is suspended until one or more conditions are over.
	 * The actual value here is a bitfield of those conditions; see the
	 * BEV_SUSPEND_* flags above. */
	bufferevent_suspend_flags read_suspended; //如果被设置,表明bufferevent的读事件监听被挂起,设置的值表示读事件监听被挂起的原因

	/** If set, writing is suspended until one or more conditions are over.
	 * The actual value here is a bitfield of those conditions; see the
	 * BEV_SUSPEND_* flags above. */
	bufferevent_suspend_flags write_suspended;//如果被设置,表明bufferevent的写事件监听被挂起,设置的值表示写事件监听被挂起的原因

	/** Set to the current socket errno if we have deferred callbacks and
	 * an events callback is pending. */
	int errno_pending;// socket出错时的错误信息

	/** The DNS error code for bufferevent_socket_connect_hostname */
	int dns_error;

	/** Used to implement deferred callbacks */
	struct deferred_cb deferred; //用于插入到base的延迟回调队列,用于延迟回调readcb、writecb和errorcb

	/** The options this bufferevent was constructed with */
	enum bufferevent_options options; //bufferevent的属性

	/** Current reference count for this bufferevent. */
	int refcnt;//多线程情况下可能多个线程中同时使用bufferevent,为了防止在某一个线程释放bufferevent导致其它线程不能正常工作,就设置一个引用计数,当引用计数为0表明没有线程使用这个bufferevent了就可以释放了

	/** Lock for this bufferevent.  Shared by the inbuf and the outbuf.
	 * If NULL, locking is disabled. */
	void *lock;//锁变量

	/** Rate-limiting information for this bufferevent */
	struct bufferevent_rate_limit *rate_limiting;
};

      在该结构体开头的英文说明中也提到了,bufferevent_private实际上就是bufferevent的一部分,但是并不对用户开放,可以看到bufferevent_private中有一个成员就是bufferevent类型的。因此,就可以把bufferevent_private理解为bufferevent的私有成员集合,而相应的“公开”的成员则都在bufferevent_private中bufferevent类型的变量中。

      接着再来看看bufferevent结构体。

bufferevent结构体

struct bufferevent {
	/** Event base for which this bufferevent was created. */
	struct event_base *ev_base;
	/** Pointer to a table of function pointers to set up how this
	    bufferevent behaves. */
	const struct bufferevent_ops *be_ops; //bufferevent的操作函数

	/** A read event that triggers when a timeout has happened or a socket
	    is ready to read data.  Only used by some subtypes of
	    bufferevent. */
	struct event ev_read;
	/** A write event that triggers when a timeout has happened or a socket
	    is ready to write data.  Only used by some subtypes of
	    bufferevent. */
	struct event ev_write;

	/** 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;   //输出缓冲区

	struct event_watermark wm_read;   //输入缓冲区的高低水位
	struct event_watermark wm_write;  //输出缓冲区的高低水位

	bufferevent_data_cb readcb;  //每次读取结束后,如果输入缓冲区的长度高于低水位,则调用readcb
	bufferevent_data_cb writecb; //每次写出结束后,如果输出缓冲区的长度低于低水位,则调用writecb
	/* This should be called 'eventcb', but renaming it would break
	 * backward compatibility */
	bufferevent_event_cb errorcb; //如果读取或者写入发生错误,就调用errorcb 
	void *cbarg; //readcb、writecb和errorcb的共用参数

	struct timeval timeout_read;  //ev_read的超时值
	struct timeval timeout_write;  //ev_write的超时值

	/** Events that are currently enabled: currently EV_READ and EV_WRITE
	    are supported. */
	short enabled;    //反映当前的bufferevent是可读还是可写
};

      其中主要有以下几个非常重要的成员变量:

1.ev_read和ev_write:用来分别监听读事件和写事件的event变量。bufferevent并没有用一个成员变量去存储它对应监听的fd,这是没有必要的,因为event类型的ev_read和ev_write变量内部本身就需要指定监听的fd;

2.input和output:evbuffer类型的两个成员变量,分别对应于读缓冲区和写缓冲区;

3.wm_read和wm_write:event_watermark结构体类型变量,用来描述缓冲区的高低水位值,因此这两个变量分别对应于读缓冲区的高低水位以及写缓冲区的高低水位;

4.readcb、writecb和errorcb:三者各自对应了一个回调函数,分别对应于读缓冲区发生一次读操作写缓冲区发生一次写操作以及发生特殊事件这三种情况。

     

     以上大概知道了bufferevent及bufferevent_private的构成,接下来就通过bufferevent的应用来分析一下各个成员的作用。

创建一个基于socket的bufferevent

      创建一个基于socket的bufferevent,是通过bufferevent_socket_new函数实现的,该函数定义如下:

struct bufferevent *
bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
    int options)
{
	struct bufferevent_private *bufev_p;
	struct bufferevent *bufev;

#ifdef WIN32
	if (base && event_base_get_iocp(base))
		return bufferevent_async_new(base, fd, options);
#endif

	if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)//分配一个新的bufferevent_private
		return NULL;
	//为buffevent_pri中的bufferevent创建输入输出缓冲区,绑定event_base,设置bufferevent为可写状态,保存bufferevent_ops_socket到bufferevent的options
	if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,
				    options) < 0) {//初始化绑定base和ops等信息到bufferevent_pri上
		mm_free(bufev_p);
		return NULL;
	}
	bufev = &bufev_p->bev;
	evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);//添加flag
	//读写事件均关联同一文件描述符
	event_assign(&bufev->ev_read, bufev->ev_base, fd,
	    EV_READ|EV_PERSIST, bufferevent_readcb, bufev);//设置读事件回调函数等信息
	event_assign(&bufev->ev_write, bufev->ev_base, fd,
	    EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev);//设置写事件回调函数等信息

	evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);//向输出缓冲区的回调函数队列中插入bufferevent_socket_outbuf_cb

	evbuffer_freeze(bufev->input, 0);//禁止从输入缓冲区尾部添加数据,即不能从fd中读取数据
	evbuffer_freeze(bufev->output, 1);//禁止从输出缓冲区的头部删除或添加数据,即不能向fd中写入数据

	return bufev;
}

int
bufferevent_init_common(struct bufferevent_private *bufev_private,
    struct event_base *base,
    const struct bufferevent_ops *ops,
    enum bufferevent_options options)
{
	struct bufferevent *bufev = &bufev_private->bev;//bufferevent包含在bufferevent_pri中

	if (!bufev->input) {//如果bufev_private中的bufferevent的输入缓冲区没有分配,那么就重新分配
		if ((bufev->input = evbuffer_new()) == NULL)
			return -1;
	}

	if (!bufev->output) {//如果bufev_private中的bufferevent的输出缓冲区没有分配,那么就重新分配
		if ((bufev->output = evbuffer_new()) == NULL) {
			evbuffer_free(bufev->input);//如果输出缓冲区分配失败,那么就把销毁刚才分配成功的输入缓冲区
			return -1;
		}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值