libevent(九)bufferevent
bufferevent,带buffer的event
struct bufferevent {struct event_base *ev_base;const struct bufferevent_ops *be_ops; // backendstruct 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 WIN32if (base && event_base_get_iocp(base))return bufferevent_async_new(base, fd, options); #endifif ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)return NULL;if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket,options) < 0) {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, 0);evbuffer_freeze(bufev->output, 1);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 = 0;_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) < 0)r = -1;_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) == -1)return -1;}if (event & EV_WRITE) {if (be_socket_add(&bufev->ev_write,&bufev->timeout_write) == -1)return -1;}return 0; }#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 == 0 && tv->tv_usec == 0)return event_add(ev, NULL);elsereturn 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 = 0;short what = BEV_EVENT_READING;ev_ssize_t howmuch = -1, readmax=-1;_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 != 0) {howmuch = bufev->wm_read.high - evbuffer_get_length(input);/* we somehow lowered the watermark, stop reading */if (howmuch <= 0) {bufferevent_wm_suspend_read(bufev);goto done;}}readmax = _bufferevent_get_read_max(bufev_p);if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"* uglifies this code. XXXX */howmuch = readmax;if (bufev_p->read_suspended)goto done;evbuffer_unfreeze(input, 0);res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */evbuffer_freeze(input, 0);if (res == -1) {int err = evutil_socket_geterror(fd);if (EVUTIL_ERR_RW_RETRIABLE(err))goto reschedule;/* error case */what |= BEV_EVENT_ERROR;} else if (res == 0) {/* eof case */what |= BEV_EVENT_EOF;}if (res <= 0)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 = 1;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 > 0) {if (evbuffer_drain(buf, n)<0)n = -1;}EVBUFFER_UNLOCK(buf);return (int)n; }
参考资料:
Libevent源码分析-----evbuffer结构与基本操作
Libevent源码分析-----更多evbuffer操作函数
转载于:https://www.cnblogs.com/gattaca/p/7827479.html
libevent(九)bufferevent相关推荐
- 处理大并发之五 使用libevent利器bufferevent
理大并发之五 使用libevent利器bufferevent 首先来翻译一段文章 你可能注意到随着我们代码变得越来越高效,程序也变得更加复杂.当我们产生一个进程的时候,我们没有必要为每一个链接管理一个 ...
- libevent库bufferevent事件实现socket通信
一.基础API 1. 添加 bufferevent 事件 struct bufferevent *bufferevent_socket_new(struct event_base *base, evu ...
- libevent详解与实践
文章目录 前言 编译 linux ARM 生成库 概述 标准用法 库设置 创建event base 事件通知 调度事件. I/O缓冲区 计时器 异步DNS解析 事件驱动的HTTP服务器 RPC服务器和 ...
- libevent(1)
很多时候,除了响应事件之外,应用还希望做一定的数据缓冲.比如说,写入数据的时候,通常的运行模式是: l 决定要向连接写入一些数据,把数据放入到缓冲区中 l 等待连接可以写入 l 写入尽量多的数据 l ...
- 网络库libevent、libev、libuv、libhv对比
网络库libevent.libev.libuv对比_小麒麟的成长之路-CSDN博客_libevent libuv Libevent.libev.libuv三个网络库,都是c语言实现的异步事件库Asyn ...
- Linux c 开发 - libevent
目录 一.event_base 1. 创建event_base 2. 查看IO模型 3. 销毁event_base 4. 事件循环 event loop 5. event_base的例子 二.even ...
- libevent与libev简介
libevent和libev都是c语言实现的异步事件库,主要封装了三个事件,让我们在开发时不需要关注网络IO事件对应的细节,以及定时事件中应使用的数据结构,以及何时调用定时任务,所以只需要关注简单的注 ...
- 国产网络库libhv开源四周年回顾
libhv是一个跨平台的c/c++网络库,本文写在libhv开源四周年之际,借机回顾了libhv的发展历程. github地址:https://github.com/ithewei/libhv 文章目 ...
- libevent多线程使用bufferevent的那些事
void do_accept(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen ...
最新文章
- 【直播】王茂霖:二手车交易价格预测 Baseline 提高(河北高校数据挖掘邀请赛)
- 华为全球最快AI训练集群Atlas 900诞生
- 聊聊rocketmq的ProducerImpl
- 为VSFTP用户指定登录后的目录.原创测试通过.
- runtime运行时编程一些相关知识
- Win10远程桌面失败,这可能是由于CredSSP加密Oracle修正 解决方法
- python知识点博客园_python零碎知识点一
- Vue+Openlayers+el-radio实现切换地图显示
- flink check-point save-point理解
- LeetCode 1743. 从相邻元素对还原数组(拓扑排序)
- Adobe发布基于HTML5技术的网络开发工具以解决跨平台问题
- 天津市规划局存储和灾备系统集成项目
- 2021-09-02编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。
- x-bov16 firmware android,MSD0431XX 松下
- 汽车轮毂识别定位检测
- php踩过的那些坑(2) strpos引发的血案
- 中央处理器cpu中的什么是计算机的指挥中,计算机中央处理器CPU的组成有哪些
- Python+OpenCV判断图像是黑底还是白底
- java excel 模板 替换_java替换Excel字符
- 角点检测 c语言 棋盘格,一种棋盘格角点全自动检测方法与流程