加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 1、协议说明
    • 2、解决办法
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

通信协议报错?由字节对齐引发的一场“血案”

07/23 16:26
1342
阅读需 9 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

最近在搞个网络通信协议,采用socket udp传输,运行时,居然报段错误了,经过debug,发现居然是因为字节对齐问题导致的。

这个问题在实现通信协议,是经常会遇到的问题,为了方便读者理解,我把内容做了简化,分享给大家。

1、协议说明

通信协议信令格式如下:

typedef struct protocol_msg_s{
 UINT8 msgType;
 UINT8 data1;
 UINT8 data2;
 UINT16 len;
 char data[100];
}PRO_MSG;

根据协议格式,我造了一个数据frm,代表我收到的某个信令,

 UCHAR frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

根据协议,信令的字段与原始帧对应关系如下

于是我实现了一个简单的解析代码【该代码有问题】

int main(int argc, char **argv)
{
 int ret;
 int frm_len = 0;
 UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};


 PRO_MSG *pmsg = (PRO_MSG *)frm;

 printf("devType:%02x data1:%02x data2:%02x len:%04x n",
   pmsg->msgType,
   pmsg->data1,
   pmsg->data2,
   pmsg->len);
}

编译运行后,其中len的值居然是0107,而不是0007

这其实就是因为编译器采用了字节对齐导致的,

在给pmsg->len赋值时,因为需要2个字节,

这两个字节是frm[3]、frm[4]这正好分布在两个字里,

编译器忽略了frm[3],最终将frm[4]、frm[5]合在一起赋值给了pmsg->len

为什么有字节对齐?

简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错)。

原因找打了,下面就是解决了。

2、解决办法

1. 方法1 #pragma pack()

该预处理指令用来改变对齐参数。在缺省情况下,C编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对齐参数:

使用伪指令#pragma pack (n),C编译器将按照n字节对齐。

使用伪指令#pragma pack (),取消自定义字节对齐方式。

完整代码

#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;

#define MAX_FRM_DATA_LEN 100
#pragma pack(1)
typedef struct protocol_msg_s{
 UINT8 msgType;
 UINT8 data1;
 UINT8 data2;
 UINT16 len;
 char data[MAX_FRM_DATA_LEN];
}PRO_MSG;
#pragma
int main(int argc, char **argv)
{
 int ret;
 int frm_len = 0;
 UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

 PRO_MSG *pmsg = (PRO_MSG *)frm;

 printf("devType:%02x data1:%02x data2:%02x len:%04x n",
   pmsg->msgType,
   pmsg->data1,
   pmsg->data2,
   pmsg->len);
}

2. 方法2

老老实实将收到的数据帧逐字节解析,

并填充到struct protocol_msg_s

#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;

#define MAX_FRM_DATA_LEN 100

typedef struct protocol_msg_s{
 UINT8 msgType;
 UINT8 data1;
 UINT8 data2;
 UINT16 len;
 char data[MAX_FRM_DATA_LEN];
}PRO_MSG;

int frm_parse(PRO_MSG *pmsg,UINT8 buf[],int len)
{
 int pos=0;
 pmsg->msgType = buf[pos];
 pos++;
 pmsg->data1 = buf[pos];
 pos++; 
 pmsg->data2 = buf[pos];
 pos++;
 pmsg->len = buf[pos]<<8 | buf[pos+1]<<0;
 pos+=2; 

 if(pmsg->len>MAX_FRM_DATA_LEN)
 {
  printf("frm len is longer than 100n");
  return -1;
 }
 memcpy(pmsg->data,&buf[pos],pmsg->len);
 return 0;
}
int maini(int argc, char **argv)
{
 int ret;
 int frm_len = 0;
 UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};



 PRO_MSG msg;
 PRO_MSG *pmsg = &msg;

 frm_len = sizeof(frm);
 ret = frm_parse(pmsg,frm,frm_len);
 if(ret<0)
 {
  printf("frm_parse failn");
  return -1;
 }

 printf("devType:%02x data1:%02x data2:%02x len:%04x n",
   pmsg->msgType,
   pmsg->data1,
   pmsg->data2,
   pmsg->len);
 return 0;
}


推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
TJA1043TK/1Y 1 NXP Semiconductors TJA1043 - High-speed CAN transceiver SON 14-Pin

ECAD模型

下载ECAD模型
$1.96 查看
CPC1560GSTR 1 IXYS Corporation Transistor Output SSR, 1-Channel, 3750V Isolation, ROHS COMPLIANT PACKAGE-8
$5.3 查看
LTC6993CDCB-1#TRMPBF 1 Analog Devices Inc LTC6993CDCB-1#TRMPBF

ECAD模型

下载ECAD模型
$3.09 查看

相关推荐

电子产业图谱

公众号『一口Linux』号主彭老师,拥有15年嵌入式开发经验和培训经验。曾任职ZTE,某研究所,华清远见教学总监。拥有多篇网络协议相关专利和软件著作。精通计算机网络、Linux系统编程、ARM、Linux驱动、龙芯、物联网。原创内容基本从实际项目出发,保持原理+实践风格,适合Linux驱动新手入门和技术进阶。