如何使用动态改变数据大小的结构?
仅针对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)等等)
例如,我收到了数据包(hex):
24 0B 00 07 00 00 00 AA 0D 16 1C
哪里
head = 0x24
len = 0x0B 0x00
id = 0x07 0x00 0x00 0x00
data = 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结构成员的未对齐访问。
您应该将处理函数拆分为两个不同的函数:
-
一个会丢弃所有东西,直到它找到
head
字节。 该字节通常是一个常量字节,标志着数据包的开始。 这是通过这种方式完成的,以避免在先前发送的数据包的中间开始读取(即,即发送者和收听者设备的启动顺序)。一旦找到数据包的开头,它就可以读取头,
len
和id
并接收存储在Buffer
变量中的所有数据,直到它读取end
字节或者存在缓冲区溢出,在这种情况下它只会丢弃数据然后重新开始。注意,只应在Buffer变量中写入实际数据。 所有其他字段(len,id等)可以存储在不同的变量中,或存储在仅包含
Packet information
但不包含Packet information
的struct中。 这样,您就可以从传输信息中吐出应用程序数据。另请注意,此函数不解释
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
。
最后,需要注意的是:将接收到的数据包直接转换(或memcpy)到结构中几乎不是一个好主意。 您不仅要考虑零空洞(使用__packet属性),还要考虑发送方和接收方系统的字节顺序和数据格式表示,因为如果它们不同,您最终会得到错误的值。
如果字段“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 }