查看原文
其他

协议解析中的神操作:结构体嵌入共联体

The following article is from 最后一个bug Author bug菌

关注「嵌入式大杂烩」,选择「星标公众号」一起进步!

来源:最后一个bug

1

话题引出


  • bug技术交流群一个小哥贴出了如下图片 :




  • 问到了共联体类型强制转化的问题,当时bug菌点开看到这几行代码便产生了莫名的亲切感,或许这些就是C语言的魅力所在吧。


  • 其实这种操作在通信协议中拆包、解包是非常常见的,但是bug菌也是在参加工作以后在部门的代码中get到,所以这里整理分享给各位。


2

操作解析


    有认真阅读过bug公众号文章的小伙伴,应该在其他文章中也看到过这种操作,不过比较分散,今天抽出来再好好聊聊!

1

buff直接转结构体



  • 上图是简单的协议包,包括帧头,长度和数据部分,在通信数据接收或者发送末端其都位于字节流中,比如UART通信,最后都是放到Sendbuff[N]或者RecBuff[N],然后发送出去。


  • 如果你打包好了结构体,然后一个个填充byte到发送或者接受buff,着实有点麻烦,然而通过结构体指针强制类型转换为uint8_t *pSendBuff;然后在进行字节流发送,那就简洁很多了。


  • 当然在此过程中你还要注意字节序、对齐等等,在往期的文章中bug菌都有讲到,模拟此过程参考下面代码。


参考示例:
#include <stdio.h>
#include <stdlib.h>

#pragma pack(1)
typedef struct _tag_Pack
{
    int Head;
    int Len;
    char Data[2];
} sPack;

int main(int argc, char *argv[]) {
    sPack stSendPack;
    sPack *pstRevPack = NULL;
    unsigned char *SendBuff =NULL;
    unsigned char  RevBuff[20] ={0};
    int cnt = 0;

    //模拟封包 
    stSendPack.Head = 0xFF;
    stSendPack.Len = 0x08;
    stSendPack.Data[0] = 0x1;
    stSendPack.Data[1] = 0x2;

    //模拟发送包
    SendBuff = (unsigned char *)(&stSendPack);

    printf("SendData: ") ;
    for(cnt = 0 ;cnt < sizeof(sPack);cnt++)
    {
        printf("0x%02X  ",*(SendBuff + cnt)) ;
        RevBuff[cnt] = *(SendBuff + cnt);  //这里模拟接受到数据 
    } 

     printf("\n\n");
    //模拟解包 
     pstRevPack = (sPack *)RevBuff;
     printf("pstRevPack.Head    = 0x%X\n",pstRevPack->Head) ;
     printf("pstRevPack.Len     = 0x%X\n",pstRevPack->Len) ;
     printf("pstRevPack.Data[0] = 0x%X\n",pstRevPack->Data[0]) ;
     printf("pstRevPack.Data[1] = 0x%X\n",pstRevPack->Data[1]) ;

    printf("\n欢迎关注公众号:最后一个bug\n");
    return 0;
}
运行结果:


2

buff直接转结构共联体 



  • 上一节我们谈到了接受和发送的buff直接转结构体,然后进行解包处理,而交流群里面图片中是转共联体,可以说这样的数据结构结合是非常完美的。


  • buff转结构体类型,就必须buff字节流里的格式与结构体一致,才能正确的解析,而共联体是一种复合类型结构,可以存在多种形式的数据提取,这样就可以带来更多的灵活度,下面代码在操作一波。


参考示例:
#include <stdio.h>
#include <stdlib.h>

#pragma pack(1)
typedef struct _tag_PackType1
{
    int Head;
    int Len;
    char Data[2];
} sPackType1;

typedef struct _tag_PackType2
{
    int Head;
    int Len;
    int Data[2];
} sPackType2;

typedef struct _tag_PackType3
{
    int Head;
    int Len;
    float Data[2];
} sPackType3;

//结构共联体
typedef union _tag_PackType
{
    sPackType1 stPackType1;
    sPackType2 stPackType2;
    sPackType3 stPackType3;
} uPackType;

//不同结构体类型的解析函数
void ParsePackType1(uPackType *punPackType)
{
    punPackType->stPackType1.Head = 0xF1;
    //you do something! 
}

void ParsePackType2(uPackType *punPackType)
{
    punPackType->stPackType2.Head = 0xF2;
    //you do something! 
}

void ParsePackType3(uPackType *punPackType)
{
    punPackType->stPackType3.Head = 0xF3;
    //you do something! 
}
/******************************************
 * Fuction: Buff转结构共联体 
 * Author :(公众号:最后一个bug) 
 *****************************************/
 
int main(int argc, char *argv[]) {

    uPackType *punPackType;
    unsigned char  RevBuff[20] ={0};

    //通信字节流接受到 RevBuff以后强转 
    punPackType = (uPackType*)RevBuff;

    //根据自身需要,不同的解析函数,统一传递共联体即可 
    ParsePackType1(punPackType);
    ParsePackType2(punPackType);
    ParsePackType3(punPackType); 

    printf("\n欢迎关注公众号:最后一个bug\n");
    return 0;
}


  • 以上就是今天的全部内容,如果代码都不能讲清楚,那bug菌也没办法了,enjoy!


3、结束语


     下班回家就一直肝这篇文章,有点小累,作为一名原创作者真不容易,大家且看且珍惜。


    还有读者讨论由于之前是内测,现在系统收回改造,应该要等一些时日,所以本文没法留言了!


  猜你喜欢:


嵌入式开发中C语言编程的一些要点简述


实用 | 手头上无LCD却又急着开发UI?LCD模拟器了解一下~


干货 | 嵌入式之状态机编程


在公众号聊天界面回复1024,可获取嵌入式资源;回复 ,可查看文章汇总。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存