[英]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.