【进阶】"结构体嵌入共联体"在协议解析中的神操作!
1、聊一聊
今天的文章话题引出来自bug技术交流群,主要是想把这种协议解析和设计的方式分享给大家!
2、正文部分
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、结束语
下班回家就一直肝这篇文章,有点小累,作为一名原创作者真不容易,大家且看且珍惜。
还有读者讨论由于之前是内测,现在系统收回改造,应该要等一些时日,所以本文没法留言了!
好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地,如果你喜欢交流可以添加下方bug菌微信,我拉你加入公众号技术交流群。
推荐好文 点击蓝色字体即可跳转
☞ 【MCU】寄存器、标准库、HAL库、LL库,这么多库!你叫我怎么选?