最近在搞個(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;
}