网络缓冲区随笔

网络缓冲区随笔

在服务器处理网络连接(本文中仅讨论TCP连接的情况)时,常常会构建一块 buffer,来存储收到的网络数据,直到确认收到的数据已经处理完,才会从 buffer 中清除。

从功能来看,就是开一块内存,满足不断的读写数据需求。这里不考虑恶意连接或者恶意数据包.

为了提高 buffer 效率,需要考虑的几点:

  • 多连接的情况,例如网关,会有频繁的建立、断开 TCP 连接,要考虑 buffer 内存碎片的问题.
  • 减少内存拷贝的开销.

之前常见的做法(简称实现一):为每一个 TCP 连接分配一块 buffer 大小=size,读网络数据时加到 buffer 尾部,上层取数据时从 buffer 头上取,当 buffer 之前的留白超过一定阈值(例如 size/2)时,整体做一个 memory move 的排干处理。

实现一的优劣:

  • 多连接的情况,可以通过内存池来构建 buffer,每一个 buffer 都是固定大小的,也很方便;
  • 在从 TCP 缓冲区中读取消息时,不可避免的会做一次 memcpy,不过后续处理可以只传递 buffer 的指针,不必再做 memcpy.
  • buffer 需要做排干处理,这里会增加 memcpy 的消耗。但是实际上这中情况发生的概率并不高(阈值决定了),而且 memcpy 的量也不会很大。

这两天看云风关于 ringbuffer 的一篇 blog,文中提出了另一个解决问题的方法(简称实现二):

为所有的连接分配一块 ringbuffer,将所有读取到的数据循环插入到 buffer,并以链表的方式串联起来自同一连接的数据片。 当上层处理应用时,如果数据在 buffer 中是独立的一块,直接返回内部指针. 如果需要拼接,则需要单独开一块内存拼接之后返回给上层。 代码在这里

实现二确实有它的独到之处,在我看来:

  • 内存管理方便,很明显,综合效率比每一个连接开一块 buffer 要来的高;
  • 实际情况中,收到需要重新拼接的包的情况会比较少,也就是说实际上需要多做 memcpy 的情况也很少;
  • 内部实现相对复杂,因为要维护部分连接数据,相当于上层连接对象的数据,与缓冲区的数据有一定程度的耦合;

对比上述两种 buffer 的实现,我自己的感觉,这两种实现方式可以市场经济和计划经济来类比:

  • 实现一是计划经济,固定分配,每个连接只要管好自己的一某三分地就好了;
  • 实现二是市场经济,大家分享市场,各自竞争,对于恶意的捣蛋分子,直接干掉(原文:“碰到 ring buffer 回卷后,碰到那些未废弃的数据块(尚未处理掉),索引到对应的连接,直接 close 掉连接,把没有处理的数据扔掉即可”)。需要宏观政策的调控,也意味着策略会更复杂一些。

今天突然想到针对实现一,可以做一点优化,改进成一个 circle-buffer,而不需要做排干处理。

常见的 circle-buffer 是在 buffer 到尾部时,做回绕。当碰到不连续的数据(分布在 circle-buffer 前后各一块),最大的问题就是无法保证给上层提供连续的内部指针,不可避免的需要做内存拷贝。所以实现一中采用了简单的排干策略,放弃一部分性能,为上层接口调用提供方便。

  • 假设原有的缓冲区=size,最大的协议包长度为 n,这里将缓冲区调整为 size+n.
  • 缓冲区内有读写指针,分别代表上层读取数据的下标,和读取网络数据的缓冲区下标。
  • 如果当前缓冲区的写指针在[0, size)区间,收到了可读通知,读取数据并且写指针移动到了[size, size+n)区间,此时将写指针回绕到 buffer 头部n的位置。
  • 同时触发回调,回调如果处理掉部分数据,读指针也到了[size, size+m)的位置(m <= n),则可以将[m, n)区间内的数据拷贝到buffer头部[m, n)处,并将读指针跳转到m处。

具体的步骤应该是如下图所示:

这样可以减少了 memcpy 的使用场景,但是与实现一相比,能不能带来性能提升,还有待实验考证。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林冠宏的技术文章

PHP 获取 特定时间范围 类

目录 前序   用途   功能及事项   使用方法   代码及注释 前序:   总体来说,我更应该是一个 android 移动开发者,而不是一个 phper,...

85810
来自专栏Java技术栈

史上最全阿里 Java 面试题总结

以下为大家整理了阿里巴巴史上最全的 Java 面试题,涉及大量 Java 面试知识点和相关试题。

7163
来自专栏Python中文社区

Python云计算框架:OpenStack源码分析之RabbitMQ(二)

之前发布的文章因为在编辑后代码部分在手机上看不清已被及时删除,本文重新编辑好之后再发布一次,带来不便请谅解! 專 欄 ❈ ZZR,Python中文社区专栏作者...

3079
来自专栏程序员的知识天地

使用 JS 实现一个本地数据库

前端很多时候还是需要保存一些数据的,这里的保存指的是长久的保存。以前的思想是把数据保存在 Cookie 中,或者将 key 保存在 Cookie 中,将其他数据...

3802
来自专栏玄魂工作室

Python爬虫之urllib模块1

Python爬虫之urllib模块1 本文来自网友投稿。作者PG,一个待毕业待就业二流大学生。玄魂工作室未对该文章内容做任何改变。 因为本人一直对推理悬疑比较感...

3296
来自专栏程序员宝库

购物网站的 redis 相关实现(Java)

本文主要内容: 登录cookie 购物车cookie 缓存数据库行 测试 必备知识点: WEB应用就是通过HTTP协议对网页浏览器发出的请求进行相应的服务器或者...

49714
来自专栏iKcamp

手把手教你撸一个 Webpack Loader

文:小 boy(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 ? 经常逛 webpack 官网的同学应该会很眼熟上面的图。正如它宣传的一样,w...

4584
来自专栏一枝花算不算浪漫

[Redis]Redis 概述及基本使用规范.

5038
来自专栏C/C++基础

Google C++编程风格指南(一)之头文件的相关规范

一个良好的编程规范和风格是一名程序猿成熟的标志。规范的编码可以减少代码冗余,降低出错概率,便于代码管理和代码交流等等,事实上,其作用远不止这些,我们要牢记编码规...

1491
来自专栏智能大石头

微软的软件授权及保护服务(SLPS)试用分析

这些天都在绞尽脑汁地想怎么样设计一个授权方式来保护我的组件,今天看了一下同事从广州带回来的Tech2007的讲稿,里面提到了 微软的软件授权及保护服务(SLPS...

2238

扫码关注云+社区

领取腾讯云代金券