C++ 环形缓冲区(队列)简单实现

2023-05-16

1. 说明

在实际工作中,如果数据流量过大,可以先把数据接收到数据缓冲区中,处理之后再取出。我们定义的包协议可以采用定长包,可以采用不定长度的包,环形缓冲区都能处理。

2. 使用场景

2.1 生产消费的场所

在 “生产者--消费者”模式中,往往会用到这种环形缓冲区的功能。

2.2 线程间高速通信的场所

3. 使用的局限性

主要适用于单向传输。不太适用于双向传输的情形。

4. 代码展示

CircleBuffer.h

#pragma once
# include	<stdint.h>
# include	<mutex>
/**
 * 数据包头,每次存往环形队列的数据长度可能不一致,所以每次先存一个EleHeader用于指示长度,然后再存实际的数据。
 */
struct EleHeader 
{
	int payloadLen;//实际消息的长度
};

/**
 * 此环形队列不考虑多线程问题,默认一进(putData)一出(getData)
 */

class CircleBuffer
{
public:
	CircleBuffer(uint32_t bufSize);
	~CircleBuffer();

private:
	char	*m_buf;           //指向环形缓冲区的指针
	int		m_readPos;        //缓冲区读位置索引
	int		m_writePos;       //缓冲区写位置索引
	int     m_readableSize;   //缓存区中可读的字节数(已经写但是还未读的字节数,取值范围为[0,m_maxBufSize])
	int		m_maxBufSize;     //缓冲区的最大值(根据业务不同,设置合适的值)
	int		m_packHeadLength; //数据包头长度(如果是定长包,那么这里可以是整个数据包。如果是不
							  //定长的,那么这里就是包头的长度)
	std::mutex   m_mutex;
public:
	/**
	 * 把接收到的数据拷贝到缓冲区中
	 * 返回值: -1:出错。 >=0:实际写入的字节数。
	 */
	int putData(char *data, int len);

	/**
	* 从缓冲区中获取指定长度的数据,存到buf中
	* 返回值: -1:出错。 >=0:实际获取到的字节数。
	*/
	int getData(char* buf, int len);

	//返回缓存区中的可读字节数量
	int getBufReadSize();

	//重置缓冲区    
	void	resetBuffer();   

	//缓冲区是否为空
	bool Empty();
};

CircleBuffer.cpp

#include "CircleBuffer.h"
#include <assert.h>

//源文件实现
CircleBuffer::CircleBuffer(uint32_t bufSize)
{
	m_writePos = 0;
	m_readPos = 0;
	m_readableSize = 0;
	m_maxBufSize = bufSize;
	m_buf = new char[m_maxBufSize];
	m_packHeadLength = sizeof(EleHeader);//EleHeader为"你自己定义的数据包头"。
}

CircleBuffer::~CircleBuffer()
{
	if (m_buf)
	{
		delete[] m_buf;
		m_buf = NULL;
	}
}


bool CircleBuffer::Empty()
{
	return m_readableSize == 0;
}

int CircleBuffer::putData(char *data, int len)
{
	if (len <= 0)
	{
		return 0;
	}
	//判断队列的空闲区域大小
	assert(m_readableSize >= 0 && m_readableSize <= m_maxBufSize);
	m_mutex.lock();
	int resLen = m_maxBufSize - m_readableSize;//可写的总字节数
	
	if (len > resLen)
	{
		len = resLen;
	}

	//按字节拷贝
	for (int i = 0; i < len; i++)
	{
		m_buf[m_writePos] = data[i];
		//注意这里不能用rear++,既然是环形缓冲区,如果填充到底部之后,底部的索引要回到头部
		m_writePos = (m_writePos + 1) % m_maxBufSize;
	}
	m_readableSize += len;	//可读字节数 + len
	m_mutex.unlock();
	return len;
}

int CircleBuffer::getData(char* buf, int len)
{
	if (len <= 0)
	{
		return 0;
	}
	assert(m_readableSize >= 0);
	m_mutex.lock();
	if (m_readableSize < len)
	{
		len = m_readableSize;
	}
	
	int temp = m_readPos;//获取数据的时候不能改变头部索引的值,全部处理完成之后才改变头部索引的位置
	for (int i = 0; i < len; i++)
	{
		buf[i] = m_buf[temp];
		//这里和上面一样,头部的索引也有可能跑到底部,得让它能跑回来
		temp = (temp + 1) % m_maxBufSize;
	}
	m_readPos = (m_readPos + len) % m_maxBufSize;
	m_readableSize -= len;	//可读字节数 - len
	m_mutex.unlock();
	return len;
}


//返回缓存区中的可读字节数量
int CircleBuffer::getBufReadSize()
{
	return m_readableSize;
}

//提供给使用者的接口
//重置缓冲区
void CircleBuffer::resetBuffer()
{
	m_mutex.lock();
	m_writePos = 0;
	m_readPos = 0;
	m_readableSize = 0;
	m_mutex.unlock();
}

使用测试:

#include "CircleBuffer.h"

void main()
{
	CircleBuffer container(1024*11);
	char	buf[512] = { 0 };
	EleHeader eHdr; 
	int ret = 0;
	for (int i = 0; i < 20; i++)
	{
		eHdr.payloadLen = sizeof(buf) - i;
		ret = container.putData((char*)&eHdr, sizeof(EleHeader));
		printf("%d, write size:%d\n", i+1, ret);
		ret = container.putData(buf, eHdr.payloadLen);
		printf("%d, write size:%d\n", i + 1, ret);
	}

	char readBuf[1024] = {};
	int payloadLen = 0;
	int count = 0;
	while (true)
	{
		int size = container.getBufReadSize();
		printf("can read size:%d\n", size);
		if (size <= 0)
		{
			break;
		}
		ret = container.getData(readBuf, sizeof(EleHeader));
		payloadLen = ((EleHeader*)readBuf)->payloadLen;
		printf("read EleHeader ele size:%d\n", payloadLen);

		ret = container.getData(readBuf, payloadLen);
		if (ret != payloadLen)
		{
			printf("read EleHeader payloadLen failed\n");
			break;
		}
		else
			printf("read EleHeader payloadLen ok,count:%d\n", (count++) + 1);

	}

	getchar();
	return ;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 环形缓冲区(队列)简单实现 的相关文章

  • 第三方支付架构设计之—帐户体系

    第三方支付架构设计之 帐户体系 一 xff0c 什么是第三方支付 xff1f 什么是第三方支付 xff1f 相信很多人对这个名字很熟悉 xff0c 不管是从各种媒体等都经常听到 xff0c 可以说是耳熟能熟 但 xff0c 如果非得给这个名
  • ELF文件格式详解

    ARM的可执行文件的格式是ELF格式文件 xff0c 下文对ELF格式做个详细的介绍 序言 1 OBJECT文件 导言 ELF头 ELF Header Sections String表 String Table Symbol表 Symbol
  • ROS小车(SLAM+物体追踪)

    属于交通运输工程设计的论文 xff0c 包含SLAM与物体追踪这两个方向 ROS智能车设计 一 系统描述 1 车轮子 单轮平衡式结构 xff0c 能量利用率高 xff0c 但转弯的时候需要倾角高速运动 xff0c 很难控制 差速转向平衡两轮
  • 配置apache服务器的用户认证

    经常上网的读者会遇到这种情况 xff1a 访问一些网站的某些资源时 xff0c 浏览器弹出一个对话框 xff0c 要求输入用户名和密码来获取对资源的访问 这就是用户认证的一种技术 用户认证是保护网络系统资源的第一道防线 xff0c 它控制着
  • 强大的grep用法详解:grep与正则表达式

    from http hi baidu com nearlove blog item 11db98b6b5b8aff831add1e5 html 首先要记住的是 正则表达式与通配符不一样 它们表示的含义并不相同 正则表达式只是一种表示法 只要
  • Linux中通过/proc/stat等文件计算Cpu使用率

    from xff1a http www blogjava net fjzag articles 317773 html proc文件系统 proc文件系统是一个伪文件系统 xff0c 它只存在内存当中 xff0c 而不占用外存空间 它以文件
  • 关于mysql的错误 - no query specified

    Mysql error no query specified mysql下抛出错误 xff1a error no query specified 出现此错误是sql不合法原因 xff1a 如 xff1a select from abc G
  • 详解coredump

    一 xff0c 什么是coredump 我们经常听到大家说到程序core掉了 xff0c 需要定位解决 xff0c 这里说的大部分是指对应程序由于各种异常或者bug导致在运行过程中异常退出或者中止 xff0c 并且在满足一定条件下 xff0
  • freertos 源码详解四 堆栈初始化

    在prvInitialiseNewTask函数中 xff0c 有一个步骤7 xff1a pxNewTCB gt pxTopOfStack 61 pxPortInitialiseStack pxTopOfStack pxTaskCode pv
  • STL - vector

    C 43 43 STL中的verctor好比是C语言中的数组 xff0c 但是vector又具有数组没有的一些高级功能 与数组相比 xff0c vector就是一个可以不用再初始化就必须制定大小的边长数组 xff0c 当然了 xff0c 它
  • 计算机专业术语,收藏用

    显示内存 与主板上的内存功能一样 xff0c 显存也是用于存放数据的 xff0c 只不过它存放的是显示芯片处理后的数据 显存越大 xff0c 显示卡支持的最大分辨率越大 xff0c 3D应用时的贴图精度就越高 xff0c 带3D加速功能的显
  • 7_资源文件添加

    资源文件添加 在QMainWindow中我们已经设立了一些部件 xff0c 现在我们来设置图标 通常有相对路径读取和资源文件读取两种方法 QAction actionNew 61 new QAction 基本格式 ui gt actionN
  • 智能除草机

    专利设计 xff1a 智能除草机 1 除草机背景 业界主要采用滚刀式草坪修剪车来进行运动场的除草 除草不仅浪费人力 xff0c 而且也受到天气状况的限制 传统割草机产生的环境污染也较大 随着社会经济的不断发展 xff0c 人力成本也不断攀升
  • nodejs解析http协议源码解析

    上篇文章讲到nodejs创建一个服务并且建立tcp连接的过程 接下来分析一下 xff0c 在建立tcp连接后 xff0c nodejs是如何解析http协议的 我们首先看一下nodejs在建立tcp连接时执行net js层的回调时做了什么操
  • template模板及模板类的实例化

    模板template 通常 xff0c 当我们调用一个函数时 xff0c 编译器只需要掌握函数的声明 类似的 xff0c 当我们使用一个类类型的对象时 xff0c 类定义必须是可用的 xff0c 但成员函数的定义不必已经出现 因此我们将类定
  • java学习简记录(四)

    java学习简记录 xff08 四 xff09 Java 程序是一系列对象的集合 xff0c 这些对象通过调用彼此的方法来协同工作 对象 xff1a 对象是类的一个实例 xff0c 有状态和行为 类 xff1a 类是一个模板 xff0c 它
  • VS2015+QT5.6.1环境配置后,在VS中双击无法打开*.ui文件

    在环境都搭建好以后 xff0c 在VS中新建了一个QT界面工程 双击 ui后 xff0c 期望得到Qt designer那种直接进入拖拉控件进行编辑的操作界面 but 并不能得到预期结果 解决方法如下 第1步 xff1a 在 解决方案资源管
  • 求字符串的最长回文

    主要锻炼的就是动态规划的思想 xff01 xff01 xff01 掌握这种思想 xff0c 工作中不一定用得上 xff0c 但是多一种思想就多一种可能 span class token comment dp i j 表示s的子串 xff08
  • C++查找指定目录下的特定后缀文件并按照创建时间排序

    在一个项目中 xff0c 遇到了这个需求 于是windows 43 vs平台上实现了这个功能Demo 测试完毕后移植到了具体的项目中 span class token macro property span class token dire
  • Ubuntu下Qt程序生成Core文件便于调试

    需要在运行时生成core dump文件 xff0c 以排查出错的代码行 首先在pro结尾里加入 xff1a QMAKE CC 43 61 g QMAKE CXX 43 61 g QMAKE LINK 43 61 g 在终端输入 ulimit

随机推荐

  • linux查看进程所有子进程和线程

    线程是现代操作系统上进行并行执行的一个流行的编程方面的抽象概念 当一个程序内有多个线程被叉分出用以执行多个流时 xff0c 这些线程就会在它们之间共享特定的资源 xff08 如 xff0c 内存地址空间 打开的文件 xff09 xff0c
  • 一款二进制文件查看器

    由于使用的是Notepad 43 43 64位版本 xff0c 在网上找了很多二进制查看插件HexEditor dll要么是32位不兼容 xff0c 要么是出现除零的错误 xff08 以前找到过一次支持Notepad 43 43 64位版本
  • YOLO目标检测

    一 背景 基于深度学习技术的视觉目标检测近年去的长足发展 但仍然有许多方面问题需要优化 二 YOLO算法的特点 YOLO作为一种性能优异的通用目标检测系统 xff0c 为了保证检测的效率 xff0c 提出one stage的思想 xff0c
  • (Qt中添加编译选项)QT在交叉编译时出现parameter passing for argument of type ‘std::_Rb_tree xxxxx changed in GCC 7.1

    QT版本都是5 1x 先是在Ubuntu机器上写的代码 xff0c GCC版本为5 4 xff0c 代码编译无 任何警告 后来移植到开发板 xff08 GCC版本为7 1 xff09 进行编译时 xff0c 提示这种警告 发生在代码中对st
  • C++按行读取文本并解析

    项目中需要按行读取文本文件 xff0c 并对每一行内容进行解析 每一行都是固定的字段数 xff0c 字段之间用空格隔开 span class token macro property span class token directive k
  • error C2447: “{”: 缺少函数标题(是否是老式的形式表?)

    error C2447 缺少函数标题 是否是老式的形式表 网上有人说 这个BUG是因为在win7上使用了 LF 的格式编码导致的 使用Notepad 43 43 修改成 BOM UTF8 和 windows 的 CR LF 格式一切正常 确
  • Visual Studio 2017 代码自动对齐

    点 编辑 高级 设置选定内容的格式 或者按Ctrl 43 K 然后再按Ctrl 43 F 就好了 你可以在常用快捷键自定义 窗口中进行查看 1 进入工具 选项 对话框 2 选择 环境 键盘 3 在 显示命令包含 下面的对话框中输入 对齐 关
  • CSDN 排版之颜色、字体、字号及背景色

    颜色 xff1a span class token operator lt span font color span class token operator 61 span blue span class token operator g
  • 使用QTCreator编程时,如何利用dmp文件定位程序奔溃

    写这篇文章之前 xff0c 看了一些其他人的博客 xff0c 但不是很详细 xff0c 缺这少那的 xff0c 好多都是复制粘贴别人的东西 自己动手弄了弄 xff0c 可以使用 xff0c 就记下来备忘与分享 前言 开发环境说明 编程IDE
  • Pytorch中transforms.RandomResizedCrop使用说明

    加载数据 训练集数据扩充 数据增强 和归一化 数据扩充 数据增强 的意义在于通过人为的随机范围裁剪 缩放 旋转等操作 增大训练集中的数据多样性 全面性 进而提高模型的泛化能力 训练集数据扩充和归一化 在验证集上仅需要归一化 data tra
  • C++中调用Python的办法

    1 背景 一直采用C 43 43 作为主语言开发 xff0c 最近遇到一个项目需要解析PDF文件中的文本内容 xff0c 直接采用C 43 43 来做显得不是很方便 xff0c 但用python来做就显得很简单了 难点在于如何C 43 43
  • Qt Creator远程调试嵌入式ARM开发板

    1 环境 Win10 64位系统上通过Virtual Box安装了一个Ubuntu虚拟机 ubuntu的版本 xff1a Linux kernel 4 15 0 142 generic 146 16 04 1 Ubuntu SMP Ubun
  • 套接字(描述符)读取指定的字节数

    检测fd句柄是否可读 xff0c ms毫秒超时 参数 xff1a df in 检测的句柄 ms in 超时 xff0c 毫秒 返回 xff1a 1 可读 xff0c 或者已经断开 0 超时 xff0c 仍然不可读 1 错误 int IsRe
  • 4.4线索二叉树遍历

    1 中序线索二叉树遍历 找到第一个中序遍历的结点 ThreadNode span class token operator span span class token function Firstnode span span class t
  • 自动根据本机字节序 将小端字节序的报文(字符数组)转为整数

    1 xff0c 判断本机的字节序 xff08 大端优先 小端优先 xff09 判断当前PC为大端还是小端字节序 64 返回值 xff1a 1 大端 xff1b 0 小端 int JudgeEndianOfPC int num 61 1 if
  • 智能指针的使用

    智能指针在C 43 43 11版本之后提供 xff0c 包含在头文件 lt memory gt 中 xff0c shared ptr unique ptr weak ptr 1 xff0c shared ptr的使用 shared ptr使
  • 图像检索系列——利用深度学习实现以图搜图

    转载自 xff1a 图像检索系列 利用深度学习实现以图搜图 知乎 前言 在上一篇文章 图像检索系列 利用 Python 检测图像相似度 中 xff0c 我们介绍了一个在图像检索领域非常常用的算法 感知哈希算法 这是一个很简单且快速的算法 x
  • Windows下select模型(以及EAGAIN、EWOULDBLOCK、EINTR)

    在这里记录一下 xff0c 以前都是新项目用到了就从旧项目中拷贝 自从将博客当作记事本 xff0c 发现自己多了一个好习惯 Windows下select模型 程序员攻略 CSDN博客 套接字IO超时设置和使用select实现超时管理 wj
  • RANSAC算法理解

    RANSAC是 RANdom SAmple Consensus xff08 随机抽样一致 xff09 的缩写 它可以从一组包含 局外点 的观测数据集中 xff0c 通过迭代方式估计数学模型的参数 它是一种不确定的算法 它有一定的概率得出一个
  • C++ 环形缓冲区(队列)简单实现

    1 说明 在实际工作中 xff0c 如果数据流量过大 xff0c 可以先把数据接收到数据缓冲区中 xff0c 处理之后再取出 我们定义的包协议可以采用定长包 xff0c 可以采用不定长度的包 xff0c 环形缓冲区都能处理 2 使用场景 2