libevent(九)bufferevent
bufferevent,带buffer的event
struct bufferevent {
struct event_base *ev_base;
const struct bufferevent_ops *be_ops; // backend struct event ev_read; // 读事件
struct event ev_write; // 写事件 struct evbuffer *input; // 读缓冲
struct evbuffer *output; // 写缓冲 bufferevent_data_cb readcb; // 读事件回调
bufferevent_data_cb writecb; // 写事件回调
bufferevent_event_cb errorcb; // error回调
void *cbarg; // 回调函数参数 struct event_watermark wm_read;
struct event_watermark wm_write; struct timeval timeout_read;
struct timeval timeout_write; short enabled;
};
下面简单分析bufferevent相关函数(示例DEMO)
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(, sizeof(struct bufferevent_private)))== NULL)
return NULL; if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,
options) < ) {
mm_free(bufev_p);
return NULL;
}
bufev = &bufev_p->bev;
evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD); 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); evbuffer_freeze(bufev->input, );
evbuffer_freeze(bufev->output, ); return bufev;
}
函数做了4件事:
- 设置backend: bufferevent_ops_socket
- 设置fd的读事件回调: bufferevent_readcb
- 设置fd的写事件回调: bufferevent_writecb
- 设置输出缓冲区的回调: bufferevent_socket_outbuf_cb
backend结构如下:
const struct bufferevent_ops bufferevent_ops_socket = {
"socket",
evutil_offsetof(struct bufferevent_private, bev),
be_socket_enable,
be_socket_disable,
be_socket_destruct,
be_socket_adj_timeouts,
be_socket_flush,
be_socket_ctrl,
};
bufferevent_setcb
void
bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg)
{
BEV_LOCK(bufev); bufev->readcb = readcb;
bufev->writecb = writecb;
bufev->errorcb = eventcb; bufev->cbarg = cbarg;
BEV_UNLOCK(bufev);
}
该函数主要设置用户回调函数。
bufferevent_enable
int
bufferevent_enable(struct bufferevent *bufev, short event)
{
struct bufferevent_private *bufev_private =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
short impl_events = event;
int r = ; _bufferevent_incref_and_lock(bufev);
if (bufev_private->read_suspended)
impl_events &= ~EV_READ;
if (bufev_private->write_suspended)
impl_events &= ~EV_WRITE; bufev->enabled |= event; if (impl_events && bufev->be_ops->enable(bufev, impl_events) < )
r = -; _bufferevent_decref_and_unlock(bufev);
return r;
} static int
be_socket_enable(struct bufferevent *bufev, short event)
{
if (event & EV_READ) {
if (be_socket_add(&bufev->ev_read,&bufev->timeout_read) == -)
return -;
}
if (event & EV_WRITE) {
if (be_socket_add(&bufev->ev_write,&bufev->timeout_write) == -)
return -;
}
return ;
} #define be_socket_add(ev, t) \
_bufferevent_add_event((ev), (t)) int
_bufferevent_add_event(struct event *ev, const struct timeval *tv)
{
if (tv->tv_sec == && tv->tv_usec == )
return event_add(ev, NULL);
else
return event_add(ev, tv);
}
该函数将fd加入到epoll中。
事件流程
当fd上有读事件发生时,首先调用bufferevent_readcb。
static void
bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
struct bufferevent_private *bufev_p =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
struct evbuffer *input;
int res = ;
short what = BEV_EVENT_READING;
ev_ssize_t howmuch = -, readmax=-; _bufferevent_incref_and_lock(bufev); if (event == EV_TIMEOUT) {
/* 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 */
what |= BEV_EVENT_TIMEOUT;
goto error;
} input = bufev->input; /*
* If we have a high watermark configured then we don't want to
* read more data than would make us reach the watermark.
*/
if (bufev->wm_read.high != ) {
howmuch = bufev->wm_read.high - evbuffer_get_length(input);
/* we somehow lowered the watermark, stop reading */
if (howmuch <= ) {
bufferevent_wm_suspend_read(bufev);
goto done;
}
}
readmax = _bufferevent_get_read_max(bufev_p);
if (howmuch < || howmuch > readmax) /* The use of -1 for "unlimited"
* uglifies this code. XXXX */
howmuch = readmax;
if (bufev_p->read_suspended)
goto done; evbuffer_unfreeze(input, );
res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
evbuffer_freeze(input, ); if (res == -) {
int err = evutil_socket_geterror(fd);
if (EVUTIL_ERR_RW_RETRIABLE(err))
goto reschedule;
/* error case */
what |= BEV_EVENT_ERROR;
} else if (res == ) {
/* eof case */
what |= BEV_EVENT_EOF;
} if (res <= )
goto error; _bufferevent_decrement_read_buckets(bufev_p, res); /* Invoke the user callback - must always be called last */
if (evbuffer_get_length(input) >= bufev->wm_read.low)
_bufferevent_run_readcb(bufev); goto done; reschedule:
goto done; error:
bufferevent_disable(bufev, EV_READ);
_bufferevent_run_eventcb(bufev, what); done:
_bufferevent_decref_and_unlock(bufev);
}
通过evbuffer_read(内部调用recv),将fd上的数据读入输入缓冲input中。
通过_bufferevent_run_readcb调用用户回调函数
(如果出错,通过_bufferevent_run_eventcb调用errorcb)
void
_bufferevent_run_readcb(struct bufferevent *bufev)
{
/* Requires that we hold the lock and a reference */
struct bufferevent_private *p =
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
if (bufev->readcb == NULL)
return;
if (p->options & BEV_OPT_DEFER_CALLBACKS) {
p->readcb_pending = ;
if (!p->deferred.queued)
SCHEDULE_DEFERRED(p);
} else {
bufev->readcb(bufev, bufev->cbarg);
}
}
在用户回调函数中,可以通过 bufferevent_read 取出输入缓冲input中的数据。
size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
return (evbuffer_remove(bufev->input, data, size));
} /* Reads data from an event buffer and drains the bytes read */
int
evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
{
ev_ssize_t n;
EVBUFFER_LOCK(buf);
n = evbuffer_copyout(buf, data_out, datlen);
if (n > ) {
if (evbuffer_drain(buf, n)<)
n = -;
}
EVBUFFER_UNLOCK(buf);
return (int)n;
}
参考资料:
Libevent源码分析-----evbuffer结构与基本操作
Libevent源码分析-----更多evbuffer操作函数
libevent(九)bufferevent的更多相关文章
- libevent(十)bufferevent 2
接上文libevent(九)bufferevent 上文主要讲了bufferevent如何监听读事件,那么bufferevent如何监听写事件呢? 对于一个fd,只要它的写缓冲区没有满,就会触发写事件 ...
- 处理大并发之五 使用libevent利器bufferevent
转自:http://blog.csdn.net/feitianxuxue/article/details/9386843 处理大并发之五 使用libevent利器bufferevent 首先来翻译一段 ...
- libevent(九)evhttp
用libevent构建一个http server非常方便,可参考libevent(六)http server. 主要涉及的一个结构体是evhttp: struct evhttp { /* Next v ...
- libevent+bufferevent总结
libevent+bufferevent总结 1 学习参考网址 libevent学习网址:http://blog.csdn.net/feitianxuxue/article/details/93725 ...
- libevent中的bufferevent原理
以前的文章看过缓冲区buffer了,libevent用bufferevent来负责管理缓冲区与buffer读写事件. 今天就带大家看下evbuffer.c,使用bufferevent处理事 ...
- libevent网络编程汇总
libevent源码剖析: ========================================================== 1.libevent源码剖析一(序) 2.libeve ...
- Libevent:7Bufferevents基本概念
很多时候,应用程序除了能响应事件之外,还希望能够处理一定量的数据缓存.比如,当写数据的时候,一般会经历下列步骤: l 决定向一个链接中写入一些数据:将数据放入缓冲区中: l 等待该链接变得可写: ...
- 项目中的Libevent(多线程)
多线程版Libevent //保存线程的结构体 struct LibeventThread { LibEvtServer* that; //用作传参 std::shared_ptr<std::t ...
- reactor模型框架图和流程图 libevent
学习libevent有助于提升程序设计功力,除了网络程序设计方面外,libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏.函数指针.c语言的多态支持.链表和堆等等,都有助于提升自身 ...
随机推荐
- foreach-- for循环的增强型:for(类型 变量 : 数组对象) { 使用变量 }
/* * foreach:增强for循环,一般用于遍历集合或者数组 * 格式: * for(元素的类型 变量 : 集合或者数组对象) { * 可以直接使用变量; * } 注意:在增强for循环中不能修 ...
- coding++:漫画版-了解什么是分布式事务?
————— 第二天 ————— ———————————— 假如没有分布式事务: 在一系列微服务系统当中,假如不存在分布式事务,会发生什么呢?让我们以互联网中常用的交易业务为例子: 上图中包含了库存 ...
- 【高并发】不废话,言简意赅介绍BlockingQueue
写在前面 最近,有不少网友留言提问:在Java的并发编程中,有个BlockingQueue,它是个阻塞队列,为何要在并发编程里使用BlockingQueue呢?好吧,今天,就临时说一下Blocking ...
- animation-play-state 在 ios 中不生效的解决办法(JS篇)
我们要实现动画的播放和暂停,animation-play-state 在安卓端可以使用,但是在 ios 中不起作用,这时可以使用 js 来实现相同效果. 原理 通过 js 获取当前元素的 transf ...
- d3.js v4曲线图的拖拽功能实现Zoom
zoom缩放案例 源码:https://github.com/HK-Kevin/d...:demo:https://hk-kevin.github.io/d3...: 原理:通过zoom事件来重新绘制 ...
- 这价格看得我偷偷摸了泪——用python爬取北京二手房数据
如果想了解更多关于python的应用,可以私信我,或者加群,里面到资料都是免费的 http://t.cn/A6Zvjdun 近期,有个朋友联系我,想统计一下北京二手房的相关的数据,而自己用Excel统 ...
- pandas basic cheatsheet
"胖的要死"是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的.Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具. 有多大?这篇 ...
- ASE课程总结 by 冯晓云
开始的开始,采访往届ASE班的blog:http://www.cnblogs.com/legs/p/4894362.html 和北航软工M1检查:http://www.cnblogs.com/legs ...
- Feature list, Standard and Test plan for BETA Release 12/22/2015
===================BETA RELEASE FEATRURE LIST==================== 1. Log in and account manager for ...
- J - The sum problem
Given a sequence 1,2,3,......N, your job is to calculate all the possible sub-sequences that the sum ...