仅针对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(无符号字符),依此类推)
例如,我收到了数据包(十六进制):
24 0B 00 07 00 00 00 AA 0D 16 1C
其中
head = 0x24
len = 0x0B 0x00
id = 0x07 0x00 0x00 0x00
数据= 0xAA
end = 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数据。
答案 0 :(得分:2)
结构中间不能有动态缓冲区。
解决问题的另一种方法是将结构分为两部分。
例如(注意data
在这里是flexible array member):
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结构的成员使用未对齐的访问。
答案 1 :(得分:0)
如果“ 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
}
答案 2 :(得分:0)
您应该将处理功能分为两个不同的功能:
将丢弃所有内容,直到找到head
字节为止。该字节通常是一个恒定字节,标记数据包的开始。这样做是为了避免在先前发送的数据包中间开始读取(请考虑发送者和侦听器设备的启动顺序)。
一旦找到数据包的开头,它就可以读取标头len
和id
并接收将其存储到Buffer
变量中的所有数据,直到它读取{ {1}}字节或缓冲区溢出,在这种情况下,它将丢弃数据并重新开始。
请注意,仅应将实际数据写入Buffer变量。其他所有字段(len,id等)可以存储在不同的变量中,也可以存储在仅包含end
但不包含数据的结构中。这样,您可以从传输信息中吐出应用程序数据。
还请注意,此函数不会解释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);
准备Packet报头,并从info
读取数据。
最后,要提一个警告:直接将接收到的数据包强制转换(或memcpy)到结构中绝不是一个好主意。您不仅要考虑零孔(使用__packet属性),还要考虑发送方和接收方系统的字节序和数据格式表示,因为如果它们不同,最终将得到错误的值。 / p>