当前位置: 首页 > article >正文

一种简单安全的消息队列的C语言解决方案

文章概要

  • 〇、背景
  • 一、基本思路
    • 1.1 消息队列的属性要求
    • 1.2 消息队列的结构定义
    • 1.3 写入、读取数据的长度信息处理
  • 二、基本实现
    • 2.1 消息队列的初始化
    • 2.2 消息队列的写入操作
      • 2.2.1 消息队列的基础写入操作
      • 2.2.2 消息队列的写入操作
    • 2.3 消息队列的读取操作
      • 2.3.1 消息队列的基础读取操作
      • 2.3.2 消息队列的读取操作
    • 2.4 消息队列的清除操作
    • 2.5 消息队列的空间计算
      • 2.5.1 消息队列的空闲长度
      • 2.5.2 消息队列的已使用长度
  • 三、功能测试
    • 3.1 功能测试代码
    • 3.2 测试效果显示
  • 四、源码下载

〇、背景

  基于嵌入式编程,对于有安全等级要求的项目,一般都会对编码有诸多安全性考虑的规则限制。在实际的编程中,消息队列的使用还是比较频繁的,但是对于不使用操作系统的情况下,消息队列相关的功能就需要手动实现。下面将会介绍一种简单的、安全等级较高的消息队列的实现方式。

一、基本思路

1.1 消息队列的属性要求

  作为一个存储用的消息队列,一般情况下需要几个属性。

  • 消息队列的读指针:也就是数据从什么位置开始读取
  • 消息队列的写指针:也就是数据从什么位置开始写入
  • 消息队列的现存数据长度:也就是读取多少数据
  • 消息队列的空闲容量:也就是目前数据缓存区剩余的大小
  • 消息队列的已使用容量:也就是目前数据缓存区使用了的大小
  • 消息队列的容量:一般情况下,此容量表示的数据缓存区的大小
  • 消息队列数据缓冲区:也就是队列用于存放数据的地方

1.2 消息队列的结构定义

  考虑到表示的精准性、使用的简洁性等方面,所以需要尽可能少的变量成员定义。本例中定义的结构如下。

/* 定义消息队列的数据类型 */
typedef uint8_t QueueDataType_t;

/* 定义消息队列的数据结构 */
typedef struct _message_queue_class_struct
{
   
    uint32_t read_pos;           /* 写队列数据指针 */
    uint32_t write_pos;          /* 读队列数据指针 */
    uint32_t queue_size;         /* 队列容量 */
    QueueDataType_t *queue_data; /* 队列数据 */
} MsgQueueClass_st;

  在如上的定义中,并没有直接定义 “消息队列的现存数据长度”“消息队列的空闲容量”“消息队列的已使用容量” 等信息变量,是因为上述信息均可以通过结构中定义的 “消息队列的读指针”“消息队列的写指针” 进行相关的运算获得,后面代码中会有相关的实现。

1.3 写入、读取数据的长度信息处理

  通过上述的消息队列的数据结构定义,不难看出来成员 queue_data 指向的是一个 uint8_t 的数据缓存区(数组),对于每一次写入操作,如何记录本次写入数据的长度其实很重要,因为本消息队列中存放的数据可能是通过多次写入的,而且每次写入的数据长度还不相同,那么在进行数据读取的时候,也不可能一下子读取缓存区中所有的数据,也不可能只读取一个随便长度的数据,这样的话整个队列管理起来就很困难。

  所以为了方便消息队列的管理,设定每一次读取数据的长度即为此数据写入的时候的长度。

  对于写入数据的长度信息的记录,比较方便记录和获取的方式就是写入到实际数据的头部,其占用的字节数可以自定义,本例中采用最常用的 uint16_t 来标识。所以:

  • 每次在消息队列中写入的数据时候,先在队列中写入本次数据的长度,然后再写入数据。
  • 每次在消息队列中读取的数据时候,先在队列中读取本次数据的长度,然后再读取目标长度数据。

二、基本实现

2.1 消息队列的初始化

  消息队列的初始化,主要就是初始化读指针、写指针、绑定数据缓冲区、初始化缓冲区容量等基础属性的设置。其实现代码也比较简单。

/*
 *  @fn             MsgQueue_Init
 *  @brief          初始化队列,队列结构体首地址,队列数据类型,函数将初始化队列数据为初始化值状态
 *  @param[in]      queue_handle       队列结构体首地址
 *  @param[in]      queue_buffer       队列数据类型
 *  @param[in]      queue_size         队列数据容量
 *  @return         true 初始化成功  false 初始化失败
 */
bool MsgQueue_Init(MsgQueueClass_st *queue_handle, QueueDataType_t *queue_buffer, uint32_t queue_size)
{
   
    /** 参数判断 */
    if ((NULL == queue_handle) || (0U == queue_size) || (NULL == queue_buffer))
    {
   
        return false;
    }

    memset(queue_buffer, 0U, queue_size);
    /** 初始化队列数据 */
    queue_handle->read_pos = 0U;
    queue_handle->write_pos = 0U;
    queue_handle->queue_size = queue_size;
    queue_handle->queue_data = queue_buffer;

    /** 返回 */
    return true;
}

2.2 消息队列的写入操作

2.2.1 消息队列的基础写入操作

  消息队列数据的写入操作,需要考虑几个方面的问题:

  • 当前空闲容量是否足够
  • 当前写入数据位置是否需要翻转(也就是写入数据指针位置 + 写入数据的长度 是否 超过缓存区末尾)
  • 当前写入数据指针是否需要翻转

  综上所述,消息队列的写入的基础操作,编码如下。

/*
 *  @fn         MsgQueue_WriteData
 *  @brief      队列写操作,输入队列结构体首地址、待写入数据和长度,函数将数据存在队列中
 *  @param[in]  queue_handle        队列结构体首地址
 *  @param[in]  write_buffer        待写入队列的数据
 *  @param[in]  write_data_len      待写入队列的数据长度
 *  @return     NONE
 *  @exception  此函数对输入指针参数未判断是否为NULL,如果任一指针参数为NULL,则可能引发core dump的异常。所以需要调用者保证指针参数的有效性
 */
static void MsgQueue_WriteData(MsgQueueClass_st *queue_handle, const QueueDataType_t *write_buffer, uint32_t write_data_len)
{
   
    register uint32_t i, j;
    register QueueDataType_t *t_in_ptr;

    /* 由于已经能够判定有足够的空间,不需要再对 read_pos 值进行判断 */
    j = queue_handle->queue_size - (queue_handle->write_pos);
    t_in_ptr = &(queue_handle->queue_data[queue_handle->write_pos]);

    if (j > write_data_len)
    {
   
        /* 写入时可以不用翻转 */
        /* 用自加指令可以明显提高指令的执行速度 */
        for (i = 0U; i < write_data_len; i++)
        {
   
            *t_in_ptr = *write_buffer;
            t_in_ptr++;
            write_buffer++;
        }
        queue_handle->write_pos += write_data_len;
    }
    else
    {
   
        /* 写入过程中必然翻转 */
        /* 将数据从当前指针到缓冲区尾部,写入的长度为j */
        for (i = 0U; i < j; i++)
        {
   
            *t_in_ptr = *write_buffer;
            t_in_ptr++;
            write_buffer++;
        

http://www.kler.cn/news/332073.html

相关文章:

  • CDGA|利用人工智能与边缘计算显著提升数据治理效率与效果的实践案例
  • 慢病中医药膳养生食疗管理微信小程序、基于微信小程序的慢病中医药膳养生食疗管理系统设计与实现、中医药膳养生食疗管理微信小程序的开发与应用(源码+文档+定制)
  • C++发邮件:如何轻松实现邮件自动化发送?
  • 828华为云征文|华为云Flexus云服务器X实例部署 即时通讯IM聊天交友软件——高性能服务器实现120W并发连接
  • MySQL 支持行锁还是表锁?分别有哪些优缺点?破解数据库的锁之谜:MySQL行锁与表锁的博弈
  • index.html 调用 ajax
  • 【Bug】STM32F1的PB3和PB4无法正常输出
  • 【案例73】Uclient无法读取https地址添加应用
  • MySQL 实验 3:创建数据表
  • 滚雪球学MySQL[8.2讲]:MySQL事件调度详解:定时任务的创建与管理及使用场景
  • 【web安全】——信息收集
  • cmd命令大全详解
  • Windows11系统下SkyWalking环境搭建教程
  • iSTFT 完美重构的条件详解
  • React -AppVarContext.Provider 提供者组件
  • 【回眸】Tessy 单元测试软件使用指南(四)常见报错及解决方案与批量初始化的经验
  • 浅聊前后端分离开发和前后端不分离开发模式
  • Java - LeetCode面试经典150题 - 哈希表 (二)
  • SEO友好的wordpress模板 应该具体哪些特征
  • 【SVN】一文读懂Subversion(SVN)