良许Linux教程网 干货合集 自定义协议解析组包中,大小端问题如何处理?

自定义协议解析组包中,大小端问题如何处理?

在嵌入式产品开发中,常常会遇到不同设备之间的通信、设备与服务器的通信以及设备和上位机的通信等问题。这些通信往往需要使用自定义的协议进行解析和组包。

例如,我们可以思考以下这种自定义协议的情况:

帧头1 帧头2 字段1 字段2 校验
固定值:0x55 固定值:0xAA 设备ID 电压值 前面所有数据异或值
char char short float char
1字节 1字节 2字节 4字节 1字节

数据在发送时涉及到一个大小端的概念,大小端是针对多字节数据的传输,比如上述协议中字段1,假设两字节内容为0x0001,先发送0x01后发送0x00,称为小端模式;先发送0x00后发送0x01,称为大端模式。

假设字段1内容为0x001,字段2内容为0x40533333 (对应为3.3)

假设按照小端方式发送,下面是帧数据:

55 AA 01 00 33 33 53 40 ED

下面来看看如何解析,

若干年前,在第一次面对这种问题时,用的如下傻瓜式的代码方式实现:

#include 

int main()
{
    unsigned char Rxbuf[9] = {0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};

    short DeviceId;
    float Voltage;

    unsigned char check = 0;
    int i;

    for(i=0;iif(Rxbuf[0]==0x55 && Rxbuf[1]==0xAA && Rxbuf[8]==check )
    {
        DeviceId=(Rxbuf[3]float *)&Rxbuf[4]);

        printf("DeviceId:%d\n",DeviceId);
        printf("Voltage:%f\n",Voltage);
    }

    return 0;
}
图片
图片

简单来说就是硬来,按照数组的先后顺序逐个重组解析,如果协议比较长,代码里会充斥着很多的数组下标,一不小心就数错了。而且如果更改协议的话,代码要改动很多地方。

后来有人告诉我可以定义个结构体,然后使用memcpy函数直接复制过去就完事了:

#include 
#include 
#pragma pack(1)
struct RxFrame
{
    unsigned char header1;   
    unsigned char header2;   
    short deviceId;  
    float voltage;    
    unsigned char check;
};


int main()
{
    unsigned char Rxbuf[9] = {0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};

    struct RxFrame RxData;

    unsigned char check = 0;
    int i;

    for(i=0;iif(Rxbuf[0]==0x55 && Rxbuf[1]==0xAA && RxData.check==check )
    {
        printf("DeviceId:%d\n",RxData.deviceId);
        printf("Voltage:%f\n",RxData.voltage);
    }

    return 0;
}

嗯,的确是方便了很多。不过该方式仅适合小端传输方式。

再后来,又见到有人用如下代码实现:

#include 
#include "convert.h"

int main()
{
    unsigned char Rxbuf[9] = {0x55,0xAA,0x01,0x00,0x33,0x33,0x53,0x40,0xED};

    short DeviceId;
    float Voltage;

    unsigned char check = 0;
    int i;
    int index = 0;

    for(i=0;iif(Rxbuf[0]==0x55 && Rxbuf[1]==0xAA && Rxbuf[8]==check )
    {
        index += 2;
        ByteToShort(Rxbuf, &index, &DeviceId);
        ByteToFloat(Rxbuf, &index, &Voltage);

        printf("DeviceId:%d\n",DeviceId);
        printf("Voltage:%f\n",Voltage);
    }

    return 0;
}

其中convert.h如下:

#ifndef CONVERT_H
#define CONVERT_H

void  ShortToByte(unsigned char* dest, int* index, short value);
void  FloatToByte(char* dest, int* index, float value);

#endif // CONVERT_H

convert.c如下:

#include "convert.h"
#include 
#include 

static bool Endianflag = 0;

void ByteToShort(const unsigned char* source, int* index, short* result)
{
    int i, len = sizeof(short);
    char p[len];
    memset(p, 0, len);

    if(Endianflag == 1 )
    {
        for( i = 0; i source + *index + len - i - 1);
    }
    else
    {
        for( i = 0; i source + *index + i);
    }


    *result = *((short*)p);

    *index += len;
}

void ByteToFloat(unsigned char* source, int* index, float* result)
{
    int i, len = sizeof(float);
    char p[len];
    memset(p, 0, len);


    if(Endianflag == 1 )
    {
        for( i = 0; i source + *index + len - i - 1);
    }
    else
    {
        for( i = 0; i source + *index + i);
    }


    *result = *((float*)p);

    *index += len;
}

该方法既可以支持小端模式,也可以支持大端模式,使用起来也是比较方便。

除了上述2个函数,完整的转换包含以下函数,就是将Bytes转换为不同的数据类型,以及将不同的数据类型转换为Bytes。

#ifndef CONVERT_H
#define CONVERT_H

void  ByteToShort(const unsigned char* source, int* index, short* result);
void  ByteToInt(unsigned char* source, int* index, int* result);
void  ByteToLong(char* source, int* index, long long* result);
void  ByteToFloat(unsigned char* source, int* index, float* result);
void  ByteToDouble(unsigned char* source, int* index, double* result);
void  ByteToString(unsigned char* source, int* index, char* result, int length);


void  ShortToByte(unsigned char* dest, int* index, short value);
void  IntToByte(char* dest, int* index, int value);
void  LongToByte(char* dest, int* index, long long value);
void  FloatToByte(char* dest, int* index, float value);
void  DoubleToByte(unsigned char* dest, int* index, double value);
void  StringToByte(char* dest, int* index, int length, char* value);

#endif // CONVERT_H

组包的过程和解析的过程正好相反,这里不再赘述。你在开发中遇到这种问题,是如何处理的呢?欢迎留言讨论

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部