加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 1、協(xié)議說明
    • 2、解決辦法
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

通信協(xié)議報錯?由字節(jié)對齊引發(fā)的一場“血案”

07/23 16:26
1152
閱讀需 9 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

最近在搞個網(wǎng)絡(luò)通信協(xié)議,采用socket udp傳輸,運行時,居然報段錯誤了,經(jīng)過debug,發(fā)現(xiàn)居然是因為字節(jié)對齊問題導(dǎo)致的。

這個問題在實現(xiàn)通信協(xié)議,是經(jīng)常會遇到的問題,為了方便讀者理解,我把內(nèi)容做了簡化,分享給大家。

1、協(xié)議說明

通信協(xié)議信令格式如下:

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

根據(jù)協(xié)議格式,我造了一個數(shù)據(jù)frm,代表我收到的某個信令,

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

根據(jù)協(xié)議,信令的字段與原始幀對應(yīng)關(guān)系如下

于是我實現(xiàn)了一個簡單的解析代碼【該代碼有問題】

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);
}

編譯運行后,其中l(wèi)en的值居然是0107,而不是0007

這其實就是因為編譯器采用了字節(jié)對齊導(dǎo)致的,

在給pmsg->len賦值時,因為需要2個字節(jié),

這兩個字節(jié)是frm[3]、frm[4]這正好分布在兩個字里,

編譯器忽略了frm[3],最終將frm[4]、frm[5]合在一起賦值給了pmsg->len

為什么有字節(jié)對齊?

簡單的說內(nèi)存對齊能夠提高 cpu 讀取數(shù)據(jù)的速度,減少 cpu 訪問數(shù)據(jù)的出錯性(有些 cpu 必須內(nèi)存對齊,否則指針訪問會出錯)。

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

2、解決辦法

1. 方法1 #pragma pack()

該預(yù)處理指令用來改變對齊參數(shù)。在缺省情況下,C編譯器為每一個變量或數(shù)據(jù)單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變?nèi)笔〉膶R參數(shù):

使用偽指令#pragma?pack?(n),C編譯器將按照n字節(jié)對齊。

使用偽指令#pragma?pack?(),取消自定義字節(jié)對齊方式。

完整代碼

#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

老老實實將收到的數(shù)據(jù)幀逐字節(jié)解析,

并填充到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;
}


推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
SN65HVD230DR 1 Texas Instruments 3.3 V CAN Transceiver with Standby Mode 8-SOIC -40 to 85

ECAD模型

下載ECAD模型
$2.94 查看
AT27C256R-70JU 1 Microchip Technology Inc IC OTP 256KBIT 70NS 32PLCC

ECAD模型

下載ECAD模型
$4.08 查看
HFBR-1524Z 1 Broadcom Limited Transmitter, 1Mbps, DIP, Through Hole Mount, ROHS COMPLIANT PACKAGE

ECAD模型

下載ECAD模型
$14.54 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗和培訓(xùn)經(jīng)驗。曾任職ZTE,某研究所,華清遠(yuǎn)見教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計算機網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實際項目出發(fā),保持原理+實踐風(fēng)格,適合Linux驅(qū)動新手入門和技術(shù)進(jìn)階。