如何使用动态改变数据大小的结构?

仅针对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字节。 该字节通常是一个常量字节,标志着数据包的开始。 这是通过这种方式完成的,以避免在先前发送的数据包的中间开始读取(即,即发送者和收听者设备的启动顺序)。

    一旦找到数据包的开头,它就可以读取头, lenid并接收存储在Buffer变量中的所有数据,直到它读取end字节或者存在缓冲区溢出,在这种情况下它只会丢弃数据然后重新开始。

    注意,只应在Buffer变量中写入实际数据。 所有其他字段(len,id等)可以存储在不同的变量中,或存储在仅包含Packet information但不包含Packet information的struct中。 这样,您就可以从传输信息中吐出应用程序数据。

    另请注意,此函数不解释id字段,也不解释data字段。 它只是将这些信息发送给另一个函数,如果iddata不正确/已知,它将进行处理或丢弃。

  • 找到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 }