MessageBuffer,即消息缓冲区,是在流式缓冲区的基础上实现的针对离散消息的专用通信组件,其进一步针对“消息”进行设计改进。
在 StreamBuffer 的基础上,MessageBuffer 对每条数据的长度进行了记录。MessageBuffer每一条消息的写入增加了一个字段用来表示该条消息的长度。读取时需要一次性读出至少一条消息,否则会返回读取失败。
如下所示,在使用 MessageBuffer 时发送数据的一方在调用发送数据的接口时,组件会自动记录该条消息的长度。接收数据的一方接收到该条数据时,可以知道该条数据的长度。而 StreamBuffer 会把这些数据处理成连续的字节流,没有明确的区分它们到底属于什么结构的数据。
与 StreamBuffer 一样,MessageBuffer 适用于 一个发送者、一个接收者的数据通信场景。在多个发送者、接收者时需要添加互斥保护机制。
主要的 API 如下:
// 使用动态分配的内存创建新的消息缓冲区
MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes ); //消息缓冲区在任何时候都能够保存的总字节数(不是消息)。当消息写入消息缓冲区时,还会写入额外的大小(size_t)字节来存储消息的长度。例如存入一个 8bytes 的消息,则共使用的存储空间为 sizeof(size_t) + 8.
size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer, // 消息缓冲区的句柄。
const void *pvTxData, // 要复制到消息缓冲区的消息的指针。
size_t xDataLengthBytes, // 消息的长度。即,要从 pvTxData 复制到消息缓冲区的字节数。当消息写入消息缓冲区时,还会写入额外的大小(size_t)字节来存储消息的长度。
TickType_t xTicksToWait ); // 无法发送该条消息时,最大等待的时间。若发送成功则唤醒等待消息的任务。
size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,// 消息缓冲区的句柄。
void *pvRxData, // 指向要将收到的消息复制到的缓冲区的指针。
size_t xBufferLengthBytes, // pvRxData 参数所指向的缓冲区的长度。这将设置可以接收的消息的最大长度。如果 xBufferLengthBytes 太小而无法容纳下一条消息,则该消息将保留在消息缓冲区中,并返回 0。
TickType_t xTicksToWait ); // 等待消息的最大延时时间。
示例给出了使用 MessageBuffer 发送两种不同的离散消息的场景:
uint8_t ArrayToSend1[] = { 0, 1, 2, 3 }; // 短数据串
uint8_t ArrayToSend2[] = { 4, 5, 6, 7, 8, 9}; // 长数据串
使用 MessageBuffer 发送两种不同的离散消息的示例输出如下:
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, Minimum free heap size: 295348 bytes
TASK1: array is 1
TASK2: receive length is 4
TASK2: The buffer data is as follows:00 01 02 03
TASK1: array is 2
TASK2: receive length is 6
TASK2: The buffer data is as follows:04 05 06 07 08 09
TASK1: array is 1
TASK2: receive length is 4
TASK2: The buffer data is as follows:00 01 02 03
TASK1: array is 2
TASK2: receive length is 6
TASK2: The buffer data is as follows:04 05 06 07 08 09
TASK1: array is 1
TASK2: receive length is 4
TASK2: The buffer data is as follows:00 01 02 03
1)存储身份证号和手机号的场景中,使用 queue、stream buffer、message buffer 时的示意图如下:
队列因为只能存储固定大小的数据,它需要按照最长的数据“身份证号”分配两块空间来存储身份证号+手机号。并且接收方要接收两次数据才能接收一次完整的身份证号+手机号。
流式缓存区可以存储身份证号和手机号,但它没有记录两者的长度,不容易区分到底哪部分数据属于身份证号、哪部分属于手机号,它不适合处理离散的数据块。
消息缓存区存储身份证号、手机号,并自动地记录它们的长度,最适合处理这种离散的消息。
2)目前讲述的通信组件 queue、StreamBuffer、MessageBuffer都具备一定的缓存能力,但他们都没有提供管理数据溢出的机制,即缓冲区写满之后,再次写入数据如何处理的问题,我们将在下一节介绍一种提供这个溢出管理机制的通信组件-ringbuffer。
1)队列 queue 处理固定大小的消息,通常是一个结构,但它也可以是一个包含指针的基类型。StreamBuffer 流缓冲区将消息作为字节流进行处理。消息将作为一个单元放入队列中,并且没有任何固定大小(只是它应该适合缓冲区的最大值),并且在取出时,可以以任意长度检索获取存入的数据。消息缓冲区保存具有固定大小的离散消息,但每条消息不必具有相同的大小,并且在取出时,消息的形式与存入时完全一致。
2)MessageBuffer 与 StreamBuffer 一样,适用于 一个发送者、一个接收者的数据通信场景。在多个发送者、接收者时需要添加互斥保护机制。
1)Learning-FreeRTOS-with-esp32 系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)
3)下一篇: