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

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

通信協(xié)議報(bào)錯(cuò)?由字節(jié)對(duì)齊引發(fā)的一場(chǎng)“血案”

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

最近在搞個(gè)網(wǎng)絡(luò)通信協(xié)議,采用socket udp傳輸,運(yùn)行時(shí),居然報(bào)段錯(cuò)誤了,經(jīng)過debug,發(fā)現(xiàn)居然是因?yàn)樽止?jié)對(duì)齊問題導(dǎo)致的。

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

1、協(xié)議說明

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

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

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

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

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

于是我實(shí)現(xiàn)了一個(gè)簡(jiǎ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);
}

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

這其實(shí)就是因?yàn)?a class="article-link" target="_blank" href="/baike/1572830.html">編譯器采用了字節(jié)對(duì)齊導(dǎo)致的,

在給pmsg->len賦值時(shí),因?yàn)樾枰?個(gè)字節(jié),

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

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

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

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

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

2、解決辦法

1. 方法1 #pragma pack()

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

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

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

完整代碼

#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í)實(shí)將收到的數(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;
}


相關(guān)推薦

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

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