[英]How to use structure with dynamically changing size of data?
僅針對C的問題,C ++和向量不能解決問題。
我有這樣的結構:
typedef __packed struct Packet_s
{
U8 head;
U16 len;
U32 id;
U8 data;
U8 end;
U16 crc;
} Packet_t, *Packet_p;
( 編輯 :U8是uint8_t(unsigned char)等)
例如,我收到了packet(hex):
24 0B 00 07 00 00 00 AA 0D 16 1C
哪里
頭= 0x24
len = 0x0B 0x00
id = 0x07 0x00 0x00 0x00
數據= 0xAA
結束= 0x0D
crc = 0x16 0x1C
我可以這樣從傳入緩沖區復制它
U8 Buffer[SIZE]; // receives all bytes here
memcpy(&Packet, &Buffer, buffer_len);
並繼續努力。
如果字段“ DATA”長於1個字節,是否可以使用我的結構?
我該如何處理這樣的事情?
24 0F 00 07 00 00 00 AA BB CC DD EE 0D BD 66
數據包的長度將始終為已知(2和3個字節具有有關長度的信息)。
編輯:在“句柄”下,我的意思是我想下一步:
if (isCRCmatch() )
{
if(Packet.id == SPECIAL_ID_1)
{
// do something
}
if(Packet.id == SPECIAL_ID_2)
{
// do other
}
if(Packet.data[0] == 0xAA)
{
// do one stuff
}
if(Packet.data[1] == 0xBB && Packet.id == SPECIAL_ID_3 )
{
// do another stuff
}
}
並且(如果可能的話,我也想)使用相同的結構發送“ anwers”:
U8 rspData[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
SendResponce(Packet.id, rspData);
void SendResponce (U8 id_rsp, uint8_t* data_rsp)
{
Packet_t ResponceData;
uint16_t crc;
uint8_t *data;
ResponceData.head = 0x24;
ResponceData.len = sizeof(ResponceData); // HERE IS PROBLEM ONE
ResponceData.id = id_rsp;
ResponceData.data = *data_rsp; // HERE IS PROBLEM TWO
ResponceData.end = 0x0D; // symbol '\r'
data = (uint8_t*)&ResponceData;
crc = GetCrc(data, sizeof(ResponceData)-2); // last 2 bytes with crc
ResponceData.crc = crc;//(ack_crc >> 8 | ack_crc);
SendData((U8*)&ResponceData, sizeof(ResponceData)); // Send rsp packet
}
第一個問題-我無法自動獲得所有結構的大小,因為指針將始終為4個字節...第二個問題-我確定我會丟失rsp數據,因為我不知道它的結尾。
您不能在結構中間使用動態緩沖區。
解決該問題的另一種方法是將結構分為兩部分。 例如(注意,這里的data
是一個靈活的數組成員 ):
typedef __packed struct Packet_s
{
U8 head;
U16 len;
U32 id;
U8 data[];
} Packet_t, *Packet_p;
typedef __packed struct PacketEnd_s
{
U8 end;
U16 crc;
} PacketEnd_t, *PacketEnd_p;
然后使用
Packet_t *pPacket = (Packet_t *)&Buffer;
PacketEnd_t *pPacketEnd = (PacketEnd_t *)( count pointer here by using pPacket->len );
假設__packed允許使用對__packed結構成員的未對齊訪問。
如果字段“ DATA”長於1個字節,是否可以使用我的結構?
不可以,因為它只能容納1個data
字節。 但是您可以使用略微修改的結構版本。
typedef __packed struct Packet_s
{
U8 head;
U16 len;
U32 id;
U8 data[DATALENMAX]; // define appropriately
U8 end;
U16 crc;
} Packet_t, *Packet_p;
當然,您必須相應地調整復制:
memcpy(&Packet, &Buffer, buffer_len), memmove(&Packet.end, &Packet.data[buffer_len-7-3], 3);
關於增加的問題,有必要將數據長度傳遞給SendResponce()
:
SendResponce(rspData, sizeof rspData);
void SendResponce(uint8_t* data_rsp, int datalen)
{
Packet_t ResponceData;
uint16_t crc;
uint8_t *data;
ResponceData.head = 0x24;
ResponceData.len = 7+datalen+3; // HERE WAS PROBLEM ONE
ResponceData.id = SPECIAL_ID_X;
memcpy(ResponceData.data, data_rsp, datalen); // HERE WAS PROBLEM TWO
ResponceData.data[datalen] = 0x0D; // symbol '\r'
data = (uint8_t*)&ResponceData;
crc = GetCrc(data, 7+datalen+1); // last 2 bytes with crc
ResponceData.crc = crc;//(ack_crc >> 8 | ack_crc);
memmove(ResponceData.data+datalen+1, &ResponceData.crc, 2);
SendData((U8*)&ResponceData, ResponceData.len); // Send rsp packet
}
您應該將處理功能分為兩個不同的功能:
一種將丟棄所有內容,直到找到head
字節為止。 該字節通常是一個恆定字節,標記數據包的開始。 這樣做是為了避免在先前發送的數據包中間開始讀取(請考慮發送者和偵聽器設備的啟動順序)。
一旦找到數據包的開始,它就可以讀取標頭, len
和id
並將接收到的所有數據存儲到您的Buffer
變量中,直到讀取end
字節或緩沖區溢出為止,在這種情況下,它只會丟棄數據,然后重新開始。
注意,僅應將實際數據寫入Buffer變量。 其他所有字段(len,id等)可以存儲在不同的變量中,也可以存儲在僅包含Packet information
但不包含數據的結構中。 這樣,您可以從傳輸信息中吐出應用程序數據。
還要注意,此函數既不解釋id
字段,也不解釋data
字段。 它只是將此信息發送到另一個函數,如果id
或data
不正確/未知,它將進行處理或丟棄。
一旦找到end
字節,就可以將信息傳遞給實際的processing
功能。 它的標頭類似於:
void processPacket(U8 *data, U32 id, U16 len);
對其的調用將是:
void receiveFrame() { //Receive head //Receive id //Receive len //Fill in Buffer with the actual data //Call the processPacket function processPacket(&Buffer[0], id, len); }
一個更完整的示例可能是:
//It is not packet, since you fill it reading from the buffer and assigning
//to it, not casting the buffer into it.
//It has no data field. The data is in a different variable.
typedef struct Packet_s
{
U8 head;
U16 len;
U32 id;
U8 end;
U16 crc;
} PacketInfo_t;
U8 Buffer[MAX_BUFFER_SIZE];
PacketInfo_t info;
void receiveFrame() {
info.head=//Receive head
info.len=//Receive len
info.id=//Receive id
//Fill the buffer variable
processPacket(&Buffer[0], &info);
}
void processPacket(U8 *data, PacketInfo_t *info);
對於發送,只需使用相同的格式:
void sendPacket(U8 *data, PacketInfo_t *info);
該函數根據info
准備Packet頭,並從data
讀取data
。
最后,要提一個警告:將接收到的數據包直接投射(或存儲)到結構中絕不是一個好主意。 您不僅要考慮零孔(使用__packet屬性),還要考慮發送方和接收方系統的字節序和數據格式表示,因為如果它們不同,那么最終將得到錯誤的值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.