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件事:

  1. 设置backend: bufferevent_ops_socket
  2. 设置fd的读事件回调: bufferevent_readcb
  3. 设置fd的写事件回调: bufferevent_writecb
  4. 设置输出缓冲区的回调: 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的更多相关文章

  1. libevent(十)bufferevent 2

    接上文libevent(九)bufferevent 上文主要讲了bufferevent如何监听读事件,那么bufferevent如何监听写事件呢? 对于一个fd,只要它的写缓冲区没有满,就会触发写事件 ...

  2. 处理大并发之五 使用libevent利器bufferevent

    转自:http://blog.csdn.net/feitianxuxue/article/details/9386843 处理大并发之五 使用libevent利器bufferevent 首先来翻译一段 ...

  3. libevent(九)evhttp

    用libevent构建一个http server非常方便,可参考libevent(六)http server. 主要涉及的一个结构体是evhttp: struct evhttp { /* Next v ...

  4. libevent+bufferevent总结

    libevent+bufferevent总结 1 学习参考网址 libevent学习网址:http://blog.csdn.net/feitianxuxue/article/details/93725 ...

  5. libevent中的bufferevent原理

    以前的文章看过缓冲区buffer了,libevent用bufferevent来负责管理缓冲区与buffer读写事件.       今天就带大家看下evbuffer.c,使用bufferevent处理事 ...

  6. libevent网络编程汇总

    libevent源码剖析: ========================================================== 1.libevent源码剖析一(序) 2.libeve ...

  7. Libevent:7Bufferevents基本概念

    很多时候,应用程序除了能响应事件之外,还希望能够处理一定量的数据缓存.比如,当写数据的时候,一般会经历下列步骤: l  决定向一个链接中写入一些数据:将数据放入缓冲区中: l  等待该链接变得可写: ...

  8. 项目中的Libevent(多线程)

    多线程版Libevent //保存线程的结构体 struct LibeventThread { LibEvtServer* that; //用作传参 std::shared_ptr<std::t ...

  9. reactor模型框架图和流程图 libevent

    学习libevent有助于提升程序设计功力,除了网络程序设计方面外,libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏.函数指针.c语言的多态支持.链表和堆等等,都有助于提升自身 ...

随机推荐

  1. git tag命令

    创建本地标签 git tag -a [tagname] -m [msg] git tag -a [tag_name] [commit_id] -m [msg] 创建远程标签 git push orig ...

  2. [转] [知乎] 浅谈Roguelike

    浅谈Roguelike 从柏林诠释说起 在2008年召开的国际Roguelike开发会议上,众多的Roguelike开发者与爱好者共同制定了<柏林诠释>,规定了Roguelike游戏需要具 ...

  3. AJ学IOS 之微博项目实战(6)导航控制器NavigationController 的滑动回退功能实现

    AJ分享,必须精品 一:效果 第二篇里面写了怎样自定义navigation实现自定义的导航控制器左右按钮样式,但是当我们自己实现后,系统自带的向右边滑动来实现回退的功能就不能用了. 这里主要实现滑动回 ...

  4. 怎么快速学python?酒店女服务员一周内学会Python,一年后成为程序员

    怎么快速学python?有人说,太难!但这个女生却在一个星期内入门Python,一个月掌握python所有的基础知识点. 说出来你应该不信,刚大学毕业的女生:琳,一边在酒店打工,一边自学python, ...

  5. Android 修改应用程序字体

    在网上搜索了相关资料,研究了两种算是比较快速的改变程序字体的方法,好,先来介绍着两种方法. 首先第一种方法是重写控件(以Textview为例): 1.Android在写程序的时候谷歌早已将所有字体都默 ...

  6. linux下DNS服务器搭建,正反向解析配置

    dns服务器之前自己搭建玩过,一段时间不搞,加上当时没写文档,基本忘光光了,这次老实了,写个文档记下来,方便以后查阅. 1.服务器准备 为了避免不必要的问题,关闭防火墙,关闭selinux,hosts ...

  7. Python算法题:金字塔

    代码如下: #Python金字塔练习 """ 最大层数:max_level 当前层数:current_level 金字塔正序时: 每层的空格=最大层数-当前层数 每层的星 ...

  8. 架构师修炼之微服务部署 - Docker简介

    Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是 ...

  9. Numpy学习-(1)

    记录我学习Numpy过程 1. 介绍 (1)NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库 ...

  10. 攻防世界Web新手解析

    攻防世界入门的题目 view source 禁用右键,F12审查元素 get post hackbar进行post robots 直接访问robots.txt,发现f1ag_1s_h3re.ph文件, ...