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

  1. #include <arpa/inet.h>
  2. #include <event2/listener.h>
  3. #include <event2/bufferevent.h>
  4. #include <event2/buffer.h>
  5. #include <event2/event.h>
  6. #include <event2/util.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <stdio.h>
  10. #include <unistd.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <errno.h>
  14. #include <signal.h>
  15. //事件回调函数,处理bufferevent的事件
  16. static void conn_eventcb(struct bufferevent *bev, short events, void *ptr)
  17. {
  18. if(events & BEV_EVENT_EOF)
  19. {
  20. printf("client has closed the connection!\n");
  21. }
  22. if(events & BEV_EVENT_ERROR)
  23. {
  24. printf("got an error on the connection: %s\n", strerror(errno));
  25. }
  26. bufferevent_free(bev);
  27. }
  28. //缓存区发生变化时的回调函数
  29. static void evbuffer_cb(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *ptr)
  30. {
  31. if(info->n_added > 0)//当缓存区增加时
  32. {
  33. printf("The output buffer has added %ld bytes\n", info->n_added);
  34. evbuffer_unfreeze(buffer, 1);
  35. evbuffer_clear_flags(buffer, EVBUFFER_FLAG_DRAINS_TO_FD);
  36. evbuffer_drain(buffer, 1);
  37. evbuffer_unfreeze(buffer, 1);
  38. }
  39. if(info->n_deleted > 0)//当缓存区减少时
  40. {
  41. printf("The output buffer has deleted %ld bytes\n", info->n_deleted);
  42. }
  43. }
  44. //设置evbuffer的回调函数
  45. static void evbuffer_set(struct bufferevent *bev)
  46. {
  47. struct evbuffer *output = bufferevent_get_output(bev);
  48. struct evbuffer_cb_entry *evbuffer_callback = evbuffer_add_cb(output, evbuffer_cb, NULL);
  49. }
  50. //监听回调函数
  51. static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
  52. struct sockaddr *sa, int socklen, void *ptr)
  53. {
  54. struct event_base *base = ptr;
  55. struct bufferevent *bev = NULL;
  56. bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
  57. bufferevent_setcb(bev, NULL, NULL, conn_eventcb, NULL);
  58. bufferevent_enable(bev, EV_WRITE);
  59. bufferevent_disable(bev, EV_READ);
  60. evbuffer_set(bev);
  61. //打开一个文件,并基于文件创建一个file segment
  62. int file_segment = open("main.c", O_RDONLY);
  63. struct evbuffer_file_segment *file_seg = evbuffer_file_segment_new(file_segment, 0, -1, EVBUF_FS_CLOSE_ON_FREE);
  64. //将文件的的一段加入缓存区
  65. struct evbuffer *output = bufferevent_get_output(bev);
  66. evbuffer_add_file_segment(output, file_seg, 0, 10);
  67. }
  68. static void
  69. signal_cb(evutil_socket_t sig, short events, void *user_data)
  70. {
  71. struct event_base *base = user_data;
  72. struct timeval delay = { 2, 0 };
  73. printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
  74. event_base_loopexit(base, &delay);
  75. }
  76. int main(int argc, char *argv[])
  77. {
  78. struct event_base *base = NULL;
  79. base = event_base_new();
  80. struct sockaddr_in sin;
  81. memset(&sin, 0, sizeof(sin));
  82. sin.sin_family = AF_INET;
  83. sin.sin_port = htons(9995);
  84. struct evconnlistener *listener;
  85. listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
  86. LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (struct sockaddr *)&sin, sizeof(sin));
  87. struct event *signal_ev;
  88. signal_ev = evsignal_new(base, SIGINT, signal_cb, (void *)base);
  89. event_add(signal_ev, NULL);
  90. event_base_dispatch(base);
  91. evconnlistener_free(listener);
  92. event_free(signal_ev);
  93. event_base_free(base);
  94. return 0;
  95. }

执行过程(信号事件等无关忽略):

  • 首先在main函数中创建一个监听器,监听9995端口;
  • 监听到客户端后创建对应的bufferevent,不设置其读写回调函数,而是设置其输出缓存区的回调函数;
  • 创建一个file_segment,为main.c文件的全部字符;
  • 将file_segment段的前10个字符放入输出缓存区的尾;
  • 因为有数据进入缓存区,回调函数被调用:输出新加入的数据长度,清楚EVBUFFER_FLAG_DRAINS_TO_FD标志,然后解冻缓存区的头部,将第一个数据抛弃,最后重新冻结;
  • 因为有数据被移除,回调函数被触发:输出移除的数据(这次移除由函数evbuffer_drain触发,所以只移除一个);
  • 数据写到底层socket,再次调用回调函数;

运行结果:

  1. sunminming@sunminming:~/libevent/evbuffer$ ./main
  2. The output buffer has added 10 bytes //读进文件的前10个字符
  3. The output buffer has deleted 1 bytes //抛弃第一个
  4. The output buffer has deleted 9 bytes //正式输出剩下九个

另一个终端使用nc命令:

  1. sunminming@sunminming:~$ nc 127.0.0.1 9995
  2. include < //接收到的数据缺少第一个字符‘#’

libevent笔记3:evbuffer的更多相关文章

  1. libevent笔记6:ssl bufferevent

    Libevent另外提供了基于openssl的bufferevent来支持ssl,通过特殊的ssl bufferevent来对数据进行加密. ps:本文不对openssl相应的接口做介绍因为不熟 SS ...

  2. libevent笔记4:Filter_bufferevent过滤器

    Filter_bufferevent是一种基于bufferevent的过滤器,其本身也是一个bufferevent.能够对底层bufferevent输入缓存区中的数据进行操作(加/解密等)后再读取,同 ...

  3. libevent笔记2:Hello_World

    本篇通过libevent提供的Hello_World demo简单介绍基于libevent的TCP服务器的实现 listener listener是libevent提供的一种监听本地端口的数据结构,在 ...

  4. libevent笔记5:水位watermarks

    bufferevent中提供了对读写回调的触发条件及最大缓存长度的设置,即低高水位: 低水位:是读写回调函数的最低触发数据长度,当输入/输出缓存区中的数据长度小于低水位时,读/写回调函数不会被触发: ...

  5. libevent笔记1:安装及DEMO

    本篇简单记录了libevent的安装过程及基础的先进先出管道Demo,其中demo来自这篇博客,安装过程在这篇博客 实验环境 系统:Ubuntu 18.04.3 libevent版本:libevent ...

  6. libevent evbuffer bug

    今天发现 libevent 2.0.22 一个坑爹的bug,导致消息混乱.查找问题浪费一天,复现代码如下 #include <event2/buffer.h> #include <s ...

  7. libevent源码阅读笔记(一):libevent对epoll的封装

    title: libevent源码阅读笔记(一):libevent对epoll的封装 最近开始阅读网络库libevent的源码,阅读源码之前,大致看了张亮写的几篇博文(libevent源码深度剖析 h ...

  8. Libevent库学习笔记

    Libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,Libevent在底层select.pool.kqueue和epoll等机制基础上,封装出一致的事件接口.可 ...

  9. libevent学习笔记 一、基础知识【转】

    转自:https://blog.csdn.net/majianfei1023/article/details/46485705 欢迎转载,转载请注明原文地址:http://blog.csdn.net/ ...

随机推荐

  1. .net core vue+wangEditor (双向绑定) 上传图片和视频功能

    最终效果,是这样的,现在开始记录怎么做: 开始 npm 安装 wangEditor 安装好后, 因为要用vue 双向绑定 ,所以 我就把wangwangEditor 做成了一个封装组件,先看一下目录 ...

  2. Blend 多文本控件介绍

    原文:Blend 多文本控件介绍 多文本控件 RichTextBox FlowDocumentScrollViewer FlowDocumentPageViewer FlowDocumentReade ...

  3. Reactor的NIO线程模型

    1.Reactor单线程模型 传统的javaNIO通信的线程模型.该线程模型仅有一个I/O线程处理所有的I/O操作,如下图:   单线程模型的Reactor 所有的客户端都连接到一个I/O线程负责的A ...

  4. .NET CORE 控制台应用程序配置log4net日志文件

    使用文件格式记录日志 1.新建一个.NET CORE控制台应用程序,添加log4net.dll引用,打开工具->NuGet包管理器->管理解决方案的NuGet程序包. 2.在NuGet-解 ...

  5. C# Moq

    Moq 1 My Cases 1.1 简单入门 2 Reference 2.1 Methods 2.2 Matching Arguments 2.3 Properties 2.4 Events 2.5 ...

  6. PIE SDK主成分变换

    1.算法功能简介   主成分变换(Principal Component Analysis,PCA)又称K-L(Karhunen-Loeve)变换或霍特林(Hotelling)变换,是基于变量之间的相 ...

  7. codeforces #578(Div.2)

    codeforces #578(Div.2) A. Hotelier Amugae has a hotel consisting of 1010 rooms. The rooms are number ...

  8. 为什么你要使用这么强大的分布式消息中间件——kafka

    为什么是kafka? 在我们大量使用分布式数据库.分布式计算集群的时候,是否会遇到这样的一些问题: 我们想分析下用户行为(pageviews),以便我们设计出更好的广告位 我想对用户的搜索关键词进行统 ...

  9. http 307重定向

    刚才在做hexo页面优化,发现了本地测试返回http 307.以前没见过这个响应码,于是做一下调研. 相关文章: hexo页面优化 http 307 在rfc规范中,http 307 Temporar ...

  10. three 3D实例学习

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...