首页 > 其他分享 >如何在 libevent 中读取超过 4096 字节的数据

如何在 libevent 中读取超过 4096 字节的数据

时间:2024-02-21 18:46:40浏览次数:19  
标签:字节 read bufferevent len evbuffer libevent sizeof data 4096

如何在 libevent 中读取超过 4096 字节的数据

bufferevent 是 libevent 中相对高层的封装,较 event 使用起来方便很多。

之前有一个需求,需要从服务端读取数据进行操作,为了防止数据过大,在 bufferevent 的 read_callback 中循环调用 bufferevent_read,期望多次通过调用来读完所有的数据。

很显然,这个方法不行,第二次调用 bufferevent_read 会被阻塞,不符合预期,不能够像调用 read(2) 那样来使用。

实际上,bufferevent 内有可读数据并且大于水位 watermask 才会调用 read_callback,在 read_callback 只能调用一次 bufferevent_read 来读出缓冲区内的数据。

当一次 bufferevent_read 不能全部读取完数据怎么办,网上有人通过骚操作去修改 EVBUFFER_MAX_READ 来调大单次读取的值,然后再进行编译。不可取的行为!!!

buffervent 中的 watermask 是触发 read_callback 的关键,我们只要 bufferevent 内的数据大于设置的 watermask 即可,这样再次触发直接在 read_callback 内一次性读完。

watermask 如何设置

网络服务对数据的处理,肯定是要分包进行处理的,关闭 TCP_NODELAY 选项又会对性能造成影响。一般的解决方案是增加一个包头

bytes:          4         1          4
       +----------------+----+----------------+
       |      MAGIC     |Type|       Len      |
       +----------------+----+----------------+
  • MAGIC 为标志魔数,4字节
  • Type 为包类型,1字节
  • Len 为 Header + Payload 长度,4字节

用代码来实现这个头部的也非常简单

static const uint32_t kMessageHeaderLen = 9;
static const uint32_t kMessageHeaderMagic = 0x00114514;

enum MessageType : uint8_t {
  kMessageTypeNULL = 0,
  // ...
};

struct Message {
  uint32_t magic;
  MessageType type;
  uint32_t len;

  Message() : type(kMessageTypeNULL) {}

  Message(MessageType type, uint32_t len) : magic(kMessageHeaderMagic), type(type), len(len) {}

  Message(char data[kMessageHeaderLen]) { decode(data); }

  void decode(const char data[kMessageHeaderLen]) {
    magic = ntohl(*(uint32_t *)data);
    type = *(MessageType *)(data + sizeof(magic));
    len = ntohl(*(uint32_t *)(data + sizeof(magic) + sizeof(type))) < kMessageHeaderLen
              ? 0
              : ntohl(*(uint32_t *)(data + sizeof(magic) + sizeof(type))) - kMessageHeaderLen;
  }

  void encode(char data[kMessageHeaderLen]) {
    *(uint32_t *)data = htonl(magic);
    *(uint8_t *)(data + sizeof(magic)) = type;
    *(uint32_t *)(data + sizeof(magic) + sizeof(type)) = htonl(len + kMessageHeaderLen);
  }
};

watermask 就是这个包头中的 Len,在代码中的值为 Message::len + sizeof(Message).

如何利用 watermask 读取大于 4096 大小的数据

在第一次的 read_callback 内,先读取一个包头,将整个包的大小解析出来

  1. 如果 evbuffer 中的数据大小大于等于 Len 时,直接将所有的数据读取出来,并且要将 watermask 设置为 0(下次读取不受影响)
  2. 如果 evbuffer 中的数据大小小于 Len 时,所有还有数据没有从内核缓冲区读取到 bufferevent 内的 evbuffer 中,这个时候设置水位为 Len,在下次 read_callback 调用的时候,所有的数据都在 evbuffer 中。

代码如下:

static void read_callback(struct bufferevent *bev, void *arg) {
  struct evbuffer *evbuf = bufferevent_get_input(bev);
  size_t len = evbuffer_get_length(evbuf);
  if (len < 9)
    return;

  char head[9];
  evbuffer_copyout(evbuf, head, sizeof(head));
  Message msg(head);
  if (msg.magic != kMessageHeaderMagic) {
    evbuffer_drain(evbuf, len);
    return;
  }

  if (msg.len + 9 <= len) {
    std::vector<char> buf(msg.len, 0);
    evbuffer_remove(evbuf, head, sizeof(head));
    evbuffer_remove(evbuf, buf.data(), buf.capacity());
    bufferevent_setwatermark(bev, EV_READ, 0, 0);
    // handle data...
  } else if (msg.len + 9 > len) {
    bufferevent_setwatermark(bev, EV_READ, msg.len + 9, 0);
  }
}

其它

虽然 libevent 每次读取 4096 个字节的确性能一般,在做代理的情况下更明显,毕竟 read/epoll_ctl 的次数都要更多一些。

之前碰到上面那个问题,搜索了很多都是修改源码的 EVBUFFER_MAX_READ 来解决这种饮鸩止渴的方案,想想就不靠谱,再怎么修改都容易超过这个限制。

如果是为了提升性能,直接使用 event 来直接操作 fd,效果可能更好一些。

标签:字节,read,bufferevent,len,evbuffer,libevent,sizeof,data,4096
From: https://www.cnblogs.com/shuqin/p/18025975

相关文章

  • 马斯克称首位受试者可凭思维操控鼠标;字节低调推出视频模型丨 RTE 开发者日报 Vol.148
      开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(RealTimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑......
  • [转帖]一次搞定 Unicode、字节序、UTF-*
    茫茫人海中与你相遇相信未来的你不会很差作者:Tomson                                      来源:https://segmentfault.com/a/1190000038171151什么是字符集顾名思义,字符集就是字符的......
  • 打开java语言世界通往字节码世界的大门——ASM字节码操作类库
    一、ASM介绍1、ASM是什么ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建定制的复杂转换和代码分析工具。ASM提供了与其他Java字节码框架类似的功能,但侧重于性能。由于它的......
  • 无符号数 高低字节变换位置
     //两个字节换位quint16exchangeByte(unsignedshortval){unsignedcharh=val>>8;unsignedcharl=val&0xFF;unsignedshortret=l;ret<<=8;ret|=h;returnret;}//val变为从高到低,如0x11223344变为0x44332211quint......
  • java字节码指令
    java字节码指令  概要  众所周知,Java字节码是跨平台的,因此Java才能一次编译,处处运行。关于JVM和字节码的认识分成2个部分:  1.JVM(JavaVirtualMachine,Java虚拟机)是Java程序运行的虚拟计算机。它是Java平台的一部分,负责解释和执行Java字节码,并提供一种跨平台的运行......
  • C# 自己写的编码机制,将任意字节数组和可打印中文字符串相互转换
    正常情况下咱们可以用Base64将字节数组转成可打印字符串,但是有的时候咱们需要编码后具有一定的保密性,很明显Base64就不适用了,网上有个与熊论道就挺有意思的,于是我也研究学习了下,自己实现了一个将字节流编码为可打印(可拷贝)中文字符串的功能,反之,也能将中文字符串解码为原始字节流......
  • 字节对齐的方式
    (nsize+4096)&~4096 size+4095:首先将size加上4095,这样就可以确保在原本的size基础上,至少增加了4095个字节。&~4095:然后使用按位与操作符&结合取反操作~,这样会将size+4095的结果向下舍入到最接近的4096的整数倍。这是因为4095的二进制表示是11111111111,......
  • 字节,虽然有”大公司的病“,但咱该投还得投
    本文首发自公粽hao「林行学长」,欢迎来撩,免费领取20个求职工具资源包。了解校招、分享校招知识的学长来了!年底了,各大公司开始各种盘点了。其中最热门的还得是字节跳动,CEO梁汝波在字节年会上的发言就上了一次热搜。01字节怎么了?字节跳动作为一家TOP级别的互联网企业,相信大家都很......
  • A/B实验在字节跳动推荐系统中的应用与实践
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群本文作者为火山引擎A/B测试团队资深研发工程师,内容主要介绍A/B实验在推荐系统中的应用,并介绍了在实践中总结的几点经验,希望对做信息流推荐、电商推荐、广告推荐等方向的同学有所启发。什么是A/B实......
  • A/B实验在字节跳动推荐系统中的应用与实践
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群本文作者为火山引擎A/B测试团队资深研发工程师,内容主要介绍A/B实验在推荐系统中的应用,并介绍了在实践中总结的几点经验,希望对做信息流推荐、电商推荐、广告推荐等方向的同学有所启发。什么是......