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. 10.6 IoStudentManager

    package day11_io_student.student_demo; public class Student { private String id; private String name ...

  2. Prometheus 监控MySQL

    目录 0.简介 1.mysql_exporter部署 2.mysql报警规则 0.简介 文中主要监控MySQL/MySQL主从信息 版本:mysql-5.7,mysql_exporter-0.12.1 ...

  3. 选择IT行业的自我心得,希望能帮助到各位!(六)

    在这个社会,想做大事的人很多,但是很多事情也挺难做的,为什么说复杂的事情简单做,简单的事情重复做,这样一个人才能获得更多的优越品质,为啥说改变,人还是挺难过的,都知道本性难移,想改挺难得.在这个社会千 ...

  4. Unity 随机地图房间通道生成

    之前的博客中已经说了随机房间生成: https://www.cnblogs.com/koshio0219/p/12604383.html 但实现房间生成只是整个地图生成最初最简单的一步.下面讨论如何随 ...

  5. stand up meeting 12/29/2015

    part 组员                今日工作              工作耗时/h 明日计划 工作耗时/h    UI 冯晓云 重写popup UI添加笔记功能     6 mergeUI ...

  6. FZU 2150

    题目大意:有一个矩阵,"."表示石头,"#",表示小草,有两个人,可以在任意两个位置点燃小草,小草可以上下左右蔓延,蔓延一次的时间为1,问所有蔓延完所有小草所花 ...

  7. [复现]GXY2019

    前言 当时GXY的时候在复习中,临时抱拂脚,没时间打比赛.就写了一题./(ㄒoㄒ)/~~ babysqli 当时做了写了笔记. 过滤了or,()其中or可以用大小写绕过,可以用order by盲注 第 ...

  8. 原生Js贪吃蛇游戏实战开发笔记

    前言 本课程是通过JavaScript结合WebAPI DOM实现的一版网页游戏---贪吃蛇的开发全过程,采用面向以象的思想设计开发.通过这个小游戏的开发, 不仅可以掌握JS的语法的应用,还可以学会D ...

  9. 曹工杂谈--只用一个命令,centos系统里装了啥软件,啥时候装的,全都清清楚楚

    前言 一直以来,对linux的掌握就是半桶水的状态,经常yum装个东西,结果依赖一堆东西:然后再用源码装个东西,只知道make.make install,背后干了啥也不清楚了,卸载也不方便. 这几天工 ...

  10. java redis面试专题(附答案)

    1.什么是Redis?简述它的优缺点? Redis的全称是:Remote Dictionary.Server,本质上是一个Key-Value类型的内存数据库,很像 memcached,整个数据库统统加 ...