繁体   English   中英

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

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

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

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

    还要注意,此函数既不解释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


最后,要提一个警告:将接收到的数据包直接投射(或存储)到结构中绝不是一个好主意。 您不仅要考虑零孔(使用__packet属性),还要考虑发送方和接收方系统的字节序和数据格式表示,因为如果它们不同,那么最终将得到错误的值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM