bufferevent 流程

原文

和之前的《Libevent工作流程探究》一样,这里也是用一个例子来探究bufferevent的工作流程。具体的例子可以参考《Libevent使用例子,从简单到复杂》,这里就不列出了。其实要做的例子也就是bufferevent_socket_new、bufferevent_setcb、bufferevent_enable这几个函数。

        因为本文会用到《 Libevent工作流程探究》中提到的说法,比如将一个event插入到event_base中。所以读者最好先读一下那篇博文。此外,因为bufferevent结构体本身会使用evbuffer结构体和还会调用相应的一些操作,所以读者还应该先阅读《 evbuffer结构与基本操作》和《 更多evbuffer操作函数》。



bufferevent结构体:

        bufferevent其实也就是在event_base的基础上再进行一层封装,其本质还是离不开event和event_base,从bufferevent的结构体就可以看到这一点。

        bufferevent结构体中有两个event,分别用来监听同一个fd的可读事件和可写事件。为什么不用一个event同时监听可读和可写呢?这是因为监听可写是困难的,下面会说到原因。读者也可以自问一下,自己之前有没有试过用最原始的event监听一个fd的可写。

        由于socket 是全双工的,所以在bufferevent结构体中,也有两个evbuffer成员,分别是读缓冲区和写缓冲区。 bufferevent结构体定义如下:

  1. //bufferevent_struct.h文件  
  2. struct bufferevent {  
  3.     struct event_base *ev_base;  
  4.       
  5.     //操作结构体,成员有一些函数指针。类似struct eventop结构体  
  6.     const struct bufferevent_ops *be_ops;  
  7.   
  8.     struct event ev_read;//读事件event  
  9.     struct event ev_write;//写事件event  
  10.   
  11.     struct evbuffer *input;//读缓冲区  
  12.   
  13.     struct evbuffer *output; //写缓冲区  
  14.   
  15.     struct event_watermark wm_read;//读水位  
  16.     struct event_watermark wm_write;//写水位  
  17.   
  18.       
  19.     bufferevent_data_cb readcb;//可读时的回调函数指针  
  20.     bufferevent_data_cb writecb;//可写时的回调函数指针  
  21.     bufferevent_event_cb errorcb;//错误发生时的回调函数指针  
  22.     void *cbarg;//回调函数的参数  
  23.   
  24.     struct timeval timeout_read;//读事件event的超时值  
  25.     struct timeval timeout_write;//写事件event的超时值  
  26.   
  27.     /** Events that are currently enabled: currently EV_READ and EV_WRITE 
  28.         are supported. */  
  29.     short enabled;  
  30. };  

 

        如果看过Libevent的参考手册的话,应该还会知道bufferevent除了用于socket外,还可以用于socketpair 和 filter。如果用面向对象的思维,应从这个三个应用中抽出相同的部分作为父类,然后派生出三个子类。

        Libevent虽然是用C语言写的,不过它还是提取出一些公共部分,然后定义一个bufferevent_private结构体,用于保存这些公共部分成员。从集合的角度来说,bufferevent_private应该是bufferevent的一个子集,即一部分。但在Libevent中,bufferevent确实bufferevent_private的一个成员。下面是bufferevent_private结构体。

  1. //bufferevent-internal.h文件  
  2. struct bufferevent_private {  
  3.     struct bufferevent bev;  
  4.   
  5.     //设置input evbuffer的高水位时,需要一个evbuffer回调函数配合工作  
  6.     struct evbuffer_cb_entry *read_watermarks_cb;  
  7.   
  8.     /** If set, we should free the lock when we free the bufferevent. */  
  9.     //锁是Libevent自动分配的,还是用户分配的  
  10.     unsigned own_lock : 1;  
  11.   
  12.     ...  
  13.   
  14.     //这个socket是否处理正在连接服务器状态  
  15.     unsigned connecting : 1;  
  16.     //标志连接被拒绝  
  17.     unsigned connection_refused : 1;  
  18.   
  19.     //标志是什么原因把 读 挂起来  
  20.     bufferevent_suspend_flags read_suspended;  
  21.         //标志是什么原因把 写 挂起来  
  22.     bufferevent_suspend_flags write_suspended;  
  23.   
  24.     enum bufferevent_options options;  
  25.         int refcnt;// bufferevent的引用计数  
  26.   
  27.     //锁变量  
  28.     void *lock;  
  29. };  



新建一个bufferevent:

        函数bufferevent_socket_new可以完成这个工作。

  1. //bufferevent-internal.h文件  
  2. struct bufferevent_ops {  
  3.     const char *type;//类型名称  
  4.   
  5.     off_t mem_offset;//成员bev的偏移量  
  6.   
  7.     //启动。将event加入到event_base中  
  8.     int (*enable)(struct bufferevent *, short);  
  9.   
  10.     //关闭。将event从event_base中删除  
  11.     int (*disable)(struct bufferevent *, short);  
  12.     //销毁  
  13.     void (*destruct)(struct bufferevent *);  
  14.     //调整event的超时值  
  15.     int (*adj_timeouts)(struct bufferevent *);  
  16.     /** Called to flush data. */  
  17.     int (*flush)(struct bufferevent *, shortenum bufferevent_flush_mode);  
  18.     //获取成员的值。具体看实现  
  19.     int (*ctrl)(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);  
  20. };  
  21.   
  22.   
  23. //bufferevent_sock.c文件  
  24. const struct bufferevent_ops bufferevent_ops_socket = {  
  25.     "socket",  
  26.     evutil_offsetof(struct bufferevent_private, bev),  
  27.     be_socket_enable,  
  28.     be_socket_disable,  
  29.     be_socket_destruct,  
  30.     be_socket_adj_timeouts,  
  31.     be_socket_flush,  
  32.     be_socket_ctrl,  
  33. };  
  34.   
  35. //由于有几个不同类型的bufferevent,而且它们的enable、disable等操作是不同的。所以  
  36. //需要的一些函数指针指明某个类型的bufferevent应该使用哪些操作函数。结构体bufferevent_ops_socket  
  37. //就应运而生。对于socket,其操作函数如上。  
  38.   
  39. //bufferevent_sock.c文件  
  40. struct bufferevent *  
  41. bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,  
  42.     int options)  
  43. {  
  44.     struct bufferevent_private *bufev_p;  
  45.     struct bufferevent *bufev;  
  46.   
  47.     ...//win32  
  48.   
  49.     //结构体内存清零,所有成员都为0  
  50.     if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)  
  51.         return NULL;  
  52.   
  53.     //如果options中需要线程安全,那么就会申请锁  
  54.     //会新建一个输入和输出缓存区  
  55.     if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,  
  56.                     options) < 0) {  
  57.         mm_free(bufev_p);  
  58.         return NULL;  
  59.     }  
  60.     bufev = &bufev_p->bev;  
  61.     //设置将evbuffer的数据向fd传  
  62.     evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);  
  63.   
  64.     //将fd与event相关联。同一个fd关联两个event  
  65.     event_assign(&bufev->ev_read, bufev->ev_base, fd,  
  66.         EV_READ|EV_PERSIST, bufferevent_readcb, bufev);  
  67.     event_assign(&bufev->ev_write, bufev->ev_base, fd,  
  68.         EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev);  
  69.   
  70.     //设置evbuffer的回调函数,使得外界给写缓冲区添加数据时,能触发  
  71.     //写操作,这个回调对于写事件的监听是很重要的  
  72.     evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);  
  73.   
  74.     //冻结读缓冲区的尾部,未解冻之前不能往读缓冲区追加数据  
  75.     //也就是说不能从socket fd中读取数据  
  76.     evbuffer_freeze(bufev->input, 0);  
  77.   
  78.     //冻结写缓冲区的头部,未解冻之前不能把写缓冲区的头部数据删除  
  79.     //也就是说不能把数据写到socket fd  
  80.     evbuffer_freeze(bufev->output, 1);  
  81.   
  82.     return bufev;  
  83. }  
        留意函数里面的evbuffer_add_cb调用,后面会说到。


        函数在最后面会冻结两个缓冲区。其实,虽然这里冻结了,但实际上Libevent在读数据或者写数据之前会解冻的读完或者写完数据后,又会马上冻结。这主要防止数据被意外修改。用户一般不会直接调用evbuffer_freeze或者evbuffer_unfreeze函数。一切的冻结和解冻操作都由Libevent内部完成。还有一点要注意,因为这里只是把写缓冲区的头部冻结了。所以还是可以往写缓冲区的尾部追加数据。同样,此时也是可以从读缓冲区读取数据。这个是必须的。因为在Libevent内部不解冻的时候,用户需要从读缓冲区中获取数据(这相当于从socket fd中读取数据),用户也需要把数据写到写缓冲区中(这相当于把数据写入到socket fd中)。


        在bufferevent_socket_new函数里面会调用函数bufferevent_init_common完成公有部分的初始化。

  1. //bufferevent.c文件  
  2. int  
  3. bufferevent_init_common(struct bufferevent_private *bufev_private,  
  4.     struct event_base *base,  
  5.     const struct bufferevent_ops *ops,  
  6.     enum bufferevent_options options)  
  7. {  
  8.     struct bufferevent *bufev = &bufev_private->bev;  
  9.   
  10.     //分配输入缓冲区  
  11.     if (!bufev->input) {  
  12.         if ((bufev->input = evbuffer_new()) == NULL)  
  13.             return -1;  
  14.     }  
  15.   
  16.     //分配输出缓冲区  
  17.     if (!bufev->output) {  
  18.         if ((bufev->output = evbuffer_new()) == NULL) {  
  19.             evbuffer_free(bufev->input);  
  20.             return -1;  
  21.         }  
  22.     }  
  23.   
  24.     bufev_private->refcnt = 1;//引用次数为1  
  25.     bufev->ev_base = base;  
  26.   
  27.     /* Disable timeouts. */  
  28.     //默认情况下,读和写event都是不支持超时的  
  29.     evutil_timerclear(&bufev->timeout_read);  
  30.     evutil_timerclear(&bufev->timeout_write);  
  31.   
  32.     bufev->be_ops = ops;  
  33.   
  34.     /* 
  35.      * Set to EV_WRITE so that using bufferevent_write is going to 
  36.      * trigger a callback.  Reading needs to be explicitly enabled 
  37.      * because otherwise no data will be available. 
  38.      */  
  39.      //可写是默认支持的  
  40.     bufev->enabled = EV_WRITE;  
  41.   
  42. #ifndef _EVENT_DISABLE_THREAD_SUPPORT  
  43.     if (options & BEV_OPT_THREADSAFE) {  
  44.         //申请锁。  
  45.         if (bufferevent_enable_locking(bufev, NULL) < 0) {  
  46.             /* cleanup */  
  47.             evbuffer_free(bufev->input);  
  48.             evbuffer_free(bufev->output);  
  49.             bufev->input = NULL;  
  50.             bufev->output = NULL;  
  51.             return -1;  
  52.         }  
  53.     }  
  54. #endif  
  55.     ...//延迟调用的初始化,一般不需要用到  
  56.   
  57.     bufev_private->options = options;  
  58.   
  59.     //将evbuffer和bufferevent相关联  
  60.     evbuffer_set_parent(bufev->input, bufev);  
  61.     evbuffer_set_parent(bufev->output, bufev);  
  62.   
  63.     return 0;  
  64. }  

        代码中可以看到,默认是enable  EV_WRITE的。





设置回调函数:

        函数bufferevent_setcb完成这个工作。该函数相当简单,也就是进行一些赋值操作。

  1. //bufferevent.c文件  
  2. void  
  3. bufferevent_setcb(struct bufferevent *bufev,  
  4.     bufferevent_data_cb readcb, bufferevent_data_cb writecb,  
  5.     bufferevent_event_cb eventcb, void *cbarg)  
  6. {  
  7.     //bufferevent结构体内部有一个锁变量  
  8.     BEV_LOCK(bufev);  
  9.   
  10.     bufev->readcb = readcb;  
  11.     bufev->writecb = writecb;  
  12.     bufev->errorcb = eventcb;  
  13.   
  14.     bufev->cbarg = cbarg;  
  15.     BEV_UNLOCK(bufev);  
  16. }  

        如果不想设置某个操作的回调函数,直接设置为NULL即可。



令bufferevent可以工作:

        相信读者也知道,即使调用了bufferevent_socket_new和bufferevent_setcb,这个bufferevent还是不能工作,必须调用bufferevent_enable。为什么会这样的呢?

        如果看过之前的那些博文,相信读者知道,一个event能够工作,不仅仅需要new出来,还要调用event_add函数,把这个event添加到event_base中。在本文前面的代码中,并没有看到event_add函数的调用。所以还需要调用一个函数,把event添加到event_base中。函数bufferevent_enable就是完成这个工作的。

  1. //bufferevent.c文件  
  2. int  
  3. bufferevent_enable(struct bufferevent *bufev, short event)  
  4. {  
  5.     struct bufferevent_private *bufev_private =  
  6.         EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);  
  7.     short impl_events = event;  
  8.     int r = 0;  
  9.   
  10.     //增加引用并加锁  
  11.     //增加引用是为了防止其他线程调用bufferevent_free,释放了bufferevent  
  12.     _bufferevent_incref_and_lock(bufev);  
  13.   
  14.     //挂起了读,此时不能监听读事件  
  15.     if (bufev_private->read_suspended)  
  16.         impl_events &= ~EV_READ;  
  17.   
  18.     //挂起了写,此时不能监听写事情  
  19.     if (bufev_private->write_suspended)  
  20.         impl_events &= ~EV_WRITE;  
  21.   
  22.     bufev->enabled |= event;  
  23.   
  24.     //调用对应类型的enbale函数。因为不同类型的bufferevent有不同的e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值