首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >动态长度二进制数据解析

动态长度二进制数据解析
EN

Code Review用户
提问于 2015-06-02 01:59:25
回答 1查看 1.4K关注 0票数 5

我目前正在我的固件中为UART通信编写代码。我收到的每个字节都有一个中断(这意味着我一次不能得到一块数据,1字节,但在下面的示例中,我将它们存储在一块数据中,仅用于模拟数据解析)。

我有一个动态消息长度格式,如下面的代码所示。我可以知道解析这个消息的最有效和最优雅的方法是什么吗?我相信我现在的代码还可以进一步改进。

代码语言:javascript
复制
#include <stdint.h>

typedef struct {
    uint8_t header_DLE;
    uint16_t id;
    uint8_t msg_command;
    uint16_t msg_length;
    uint8_t data[400];
    uint8_t footer_DLE;
    uint8_t footer_BCC; 
} Serial_Rx_Data;

Serial_Rx_Data _rx_data;
static int _rx_index;
static uint8_t _rawData[512];

int main()
{
    uint8_t data[18];

    //Message format
    data[0] = 0x10;     //DLE   
    data[1] = 0x03;     //ID        
    data[2] = 0;        //ID        
    data[3] = 0x03;     //Command   
    data[4] = 0x03;     //Length - determines the data length
    data[5] = 0;        //Length    
    data[6] = 0x41;     //Data      
    data[7] = 0x42;     //Data      
    data[8] = 0x43;     //Data      
    data[9] = 0x03;     //DLE       
    data[10] = 0xfa;    //BCC

    //Simulate getting data every byte
    for (int i = 0; i <= 10; i++) {
        _rawData[_rx_index] = data[i];

        if (_rx_index == 5) {
            _rx_data.header_DLE = _rawData[0];
            _rx_data.id = _rawData[2] << 8 | _rawData[1];
            _rx_data.msg_command = _rawData[3];
            _rx_data.msg_length = _rawData[5] << 8 | _rawData[4];           
        }

        //Get the length of data
        if ((_rx_index == 6) && (_rx_data.msg_length > 0)) {
            for (int j = 0; j < _rx_data.msg_length; j++) {
                _rx_data.data[j] = _rawData[6 + j];
            }
        }

        //Get the last 2 byte
        if (_rx_index == (_rx_data.msg_length + 6 + 1)) {
            _rx_data.footer_DLE = _rawData[6 + _rx_data.msg_length];
            _rx_data.footer_BCC = _rawData[6 + _rx_data.msg_length + 1];

            //Complete parsing
            //Callback to other function
        }

        _rx_index++;
    }

    return 0;
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-06-02 09:09:35

以下是一些可以帮助您提高代码质量的东西。

不处理中断

中的消息解析

您没有显式地这样说,但是从上下文来看,您似乎试图在中断处理程序中解析串行接收的消息。这通常不是个好主意。中断处理程序应该是快速的,并且能够完成尽可能少的工作量。对于串行消息协议,这通常意味着读取每个字符,将其放入内存缓冲区(通常作为循环缓冲区实现)并更新指针。然后,不在中断中的主例程读取缓冲区的内容,并执行任何消息解析/处理。

不解析,直到您有完整的消息

在这种情况下,消息长度似乎是8个字节(6个标头+2个预告片),加上封闭式数据的大小。消息的开头似乎也有某种特殊的标记。因此,快速检查消息解析器如下所示:

  1. 至少有8个字节吗?
  2. 第一个字符是消息标记字节吗?

如果这两个问题的答案都是“否”,则不可能是真正的消息,因此数据可能会留在缓冲区中,直到收到更多的消息。如果这两个问题的答案都是“是”,那么这可能是一个信息。接下来的步骤可能是:

  1. 消息是否至少有8+msg_length字节?
  2. 校验和是对的吗?

如果这两个问题的答案都是“是”,那么您实际上可以对消息采取行动(通常将其传递给更高层的协议处理程序)。

使用状态机

像这样解析消息的典型方法是使用状态机。在这种情况下,各州可能是:

  1. 等待消息标记字节
  2. 接收报头
  3. 接收数据
  4. 接收拖车

也经常有错误和超时的状态。

避免使用“幻数”

这段代码中到处都是“魔术数字”,即未命名的常量,如400、512、10、5等。一般来说,最好避免这种情况,并给这些常量取有意义的名称。这样,如果需要更改任何内容,您就不必在代码中搜索"5“的所有实例,然后尝试确定这个特定的5是否意味着消息结构中的偏移量或具有相同值的其他常量。

缓冲区溢出

的防范

为数据分配一个固定的400个字节可能是可以的,但是如果是这样的话,那么您绝对需要在复制消息之前确保消息在这个空间中是合适的。如果不这样做,就有可能出现缓冲区溢出

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/92427

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档