libevent笔记3:evbuffer
evbuffer
之前提到bufferevent结构体提供两个缓存区用来为读写提供缓存,并自动进行IO操作。这两个缓存区是使用Libevent中的evbuffer实现的,同样,Libevent中也提供了相应的函数让我们能够直接操作evbuffer。
evbuffer的回调函数及evbuffer_cb_info结构体
我们可以为一个evbuffer增加回调函数,回调函数会在evbuffer长度有变化时被调用。evbuffer的回调函数列表中有一个evbuffer_cb_info结构体,可以用它来判断是什么事件触发了回调函数,里面包含了三个关于缓存区长度的元素:
- size_t orig_size: 表示长度变化前的缓存区长度;
- size_t n_added: 表示增加的长度;
- size_t n_deleted: 表示减少的长度;
读写evbuffer
除了使用bufferevent_write函数向缓存区读写数据外,也可以使用evbuffer提供的一些函数直接对缓存区进行读写操作。不过需要注意两点:
- 标志EVBUFFER_FLAG_DRAINS_TO_FD会阻止一般的读操作,只允许数据进入网络;
- 需要对evbuffer的头尾进行解冻(evbuffer_unfreeze)才能在头尾读写。不过从实验的结果来看,在调用对evbuffer尾增加数据的函数时,不需要额外进行冻结/解冻操作(函数listener_cb中),而在evbuffer头移除数据时需要解冻/冻结操作(函数evbuffer_cb中)。
缓存区Demo
#include <arpa/inet.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/event.h>
#include <event2/util.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
//事件回调函数,处理bufferevent的事件
static void conn_eventcb(struct bufferevent *bev, short events, void *ptr)
{
if(events & BEV_EVENT_EOF)
{
printf("client has closed the connection!\n");
}
if(events & BEV_EVENT_ERROR)
{
printf("got an error on the connection: %s\n", strerror(errno));
}
bufferevent_free(bev);
}
//缓存区发生变化时的回调函数
static void evbuffer_cb(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *ptr)
{
if(info->n_added > 0)//当缓存区增加时
{
printf("The output buffer has added %ld bytes\n", info->n_added);
evbuffer_unfreeze(buffer, 1);
evbuffer_clear_flags(buffer, EVBUFFER_FLAG_DRAINS_TO_FD);
evbuffer_drain(buffer, 1);
evbuffer_unfreeze(buffer, 1);
}
if(info->n_deleted > 0)//当缓存区减少时
{
printf("The output buffer has deleted %ld bytes\n", info->n_deleted);
}
}
//设置evbuffer的回调函数
static void evbuffer_set(struct bufferevent *bev)
{
struct evbuffer *output = bufferevent_get_output(bev);
struct evbuffer_cb_entry *evbuffer_callback = evbuffer_add_cb(output, evbuffer_cb, NULL);
}
//监听回调函数
static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *ptr)
{
struct event_base *base = ptr;
struct bufferevent *bev = NULL;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, NULL, NULL, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ);
evbuffer_set(bev);
//打开一个文件,并基于文件创建一个file segment
int file_segment = open("main.c", O_RDONLY);
struct evbuffer_file_segment *file_seg = evbuffer_file_segment_new(file_segment, 0, -1, EVBUF_FS_CLOSE_ON_FREE);
//将文件的的一段加入缓存区
struct evbuffer *output = bufferevent_get_output(bev);
evbuffer_add_file_segment(output, file_seg, 0, 10);
}
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);
}
int main(int argc, char *argv[])
{
struct event_base *base = NULL;
base = event_base_new();
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(9995);
struct evconnlistener *listener;
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (struct sockaddr *)&sin, sizeof(sin));
struct event *signal_ev;
signal_ev = evsignal_new(base, SIGINT, signal_cb, (void *)base);
event_add(signal_ev, NULL);
event_base_dispatch(base);
evconnlistener_free(listener);
event_free(signal_ev);
event_base_free(base);
return 0;
}
执行过程(信号事件等无关忽略):
- 首先在main函数中创建一个监听器,监听9995端口;
- 监听到客户端后创建对应的bufferevent,不设置其读写回调函数,而是设置其输出缓存区的回调函数;
- 创建一个file_segment,为main.c文件的全部字符;
- 将file_segment段的前10个字符放入输出缓存区的尾;
- 因为有数据进入缓存区,回调函数被调用:输出新加入的数据长度,清楚EVBUFFER_FLAG_DRAINS_TO_FD标志,然后解冻缓存区的头部,将第一个数据抛弃,最后重新冻结;
- 因为有数据被移除,回调函数被触发:输出移除的数据(这次移除由函数evbuffer_drain触发,所以只移除一个);
- 数据写到底层socket,再次调用回调函数;
运行结果:
sunminming@sunminming:~/libevent/evbuffer$ ./main
The output buffer has added 10 bytes //读进文件的前10个字符
The output buffer has deleted 1 bytes //抛弃第一个
The output buffer has deleted 9 bytes //正式输出剩下九个
另一个终端使用nc命令:
sunminming@sunminming:~$ nc 127.0.0.1 9995
include < //接收到的数据缺少第一个字符‘#’
libevent笔记3:evbuffer的更多相关文章
- libevent笔记6:ssl bufferevent
Libevent另外提供了基于openssl的bufferevent来支持ssl,通过特殊的ssl bufferevent来对数据进行加密. ps:本文不对openssl相应的接口做介绍因为不熟 SS ...
- libevent笔记4:Filter_bufferevent过滤器
Filter_bufferevent是一种基于bufferevent的过滤器,其本身也是一个bufferevent.能够对底层bufferevent输入缓存区中的数据进行操作(加/解密等)后再读取,同 ...
- libevent笔记2:Hello_World
本篇通过libevent提供的Hello_World demo简单介绍基于libevent的TCP服务器的实现 listener listener是libevent提供的一种监听本地端口的数据结构,在 ...
- libevent笔记5:水位watermarks
bufferevent中提供了对读写回调的触发条件及最大缓存长度的设置,即低高水位: 低水位:是读写回调函数的最低触发数据长度,当输入/输出缓存区中的数据长度小于低水位时,读/写回调函数不会被触发: ...
- libevent笔记1:安装及DEMO
本篇简单记录了libevent的安装过程及基础的先进先出管道Demo,其中demo来自这篇博客,安装过程在这篇博客 实验环境 系统:Ubuntu 18.04.3 libevent版本:libevent ...
- libevent evbuffer bug
今天发现 libevent 2.0.22 一个坑爹的bug,导致消息混乱.查找问题浪费一天,复现代码如下 #include <event2/buffer.h> #include <s ...
- libevent源码阅读笔记(一):libevent对epoll的封装
title: libevent源码阅读笔记(一):libevent对epoll的封装 最近开始阅读网络库libevent的源码,阅读源码之前,大致看了张亮写的几篇博文(libevent源码深度剖析 h ...
- Libevent库学习笔记
Libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,Libevent在底层select.pool.kqueue和epoll等机制基础上,封装出一致的事件接口.可 ...
- libevent学习笔记 一、基础知识【转】
转自:https://blog.csdn.net/majianfei1023/article/details/46485705 欢迎转载,转载请注明原文地址:http://blog.csdn.net/ ...
随机推荐
- vue基础之data
使用 调用data onLoad(option) { _self = this; _self.$data.xxxx = "te"; } 绑定节点 元素~~~~ <input ...
- Javascript 日历插件
1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点击下载 查看示例 2. DatePicker 这款日期插件支持单选.多选和 ...
- 写一个TimeUI显示的函数,上代码
public void FreshDateTime() { string strWeek = string.Empty; #region 格式化星期 switch (DateTime.Now.DayO ...
- 安全漏洞系列(一)---XSS漏洞解决方案(C# MVC)
参考地址:https://www.cnblogs.com/sagecheng/p/9462239.html 测试项目:MVCDemo 一.XSS漏洞定义 XSS攻击全称跨站脚本攻击,它允许恶意web用 ...
- net dll 重新签名
已经有强签名的dll或exe程序无法引用无签名的dll,这时候就需要对dll进行签名,签名的步骤如下: 为没有源码的DLL文件添加强名称 如果项目中引用了其他没有源码的dll文件,并且此dll文件是没 ...
- C#7语法快速参考-第一章 Hello World
选择IDE 要开始使用C#编程,您需要一个支持微软.NET框架的集成开发环境(IDE).最受欢迎的选择是微软自己的Visual Studio.初学可以使用Visual Studio Community ...
- 监控微信小程序中的慢HTTP请求
摘要: 请求时间太长,影响用户体验,使用 Fundebug 监控慢请求. Fundebug 的微信小程序监控插件在 0.5.0 版本已经支持监控 HTTP 请求错误,在小程序中通过wx.request ...
- 实验吧——认真一点(绕过空格,逗号,关键字过滤等 sql盲注)
题目地址:http://ctf5.shiyanbar.com/web/earnest/index.php 过滤和拦截了某些东西,我经过多次尝试,最终构造的是 1'=(ascii(mid((select ...
- android 调试崩溃Unable to instantiate application的解决方法
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.***.plants, PID: 23100 ...
- 【爬虫】网页抓包工具--Fiddler--Request和Response
[爬虫]网页抓包工具--Fiddler Fiddler基础知识 Fiddler是强大的抓包工具,它的原理是以web代理服务器的形式进行工作的,使用的代理地址是:127.0.0.1,端口默认为8888, ...