如何使用具有动态变化的数据大小的结构?

时间:2018-09-25 12:09:36

标签: c structure packet packet-capture

仅针对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数据。

3 个答案:

答案 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字节为止。该字节通常是一个恒定字节,标记数据包的开始。这样做是为了避免在先前发送的数据包中间开始读取(请考虑发送者和侦听器设备的启动顺序)。

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

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

    还请注意,此函数不会解释Packet information字段,也不会解释id字段。它只是将此信息发送到另一个函数,如果dataid不正确/未知,它将进行处理或丢弃。

  • 一旦找到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>