构建数据包的更好方法 - 字节Byte?

时间:2010-01-07 01:00:29

标签: c++ arrays serial-port

这与我今天的question asked here有关。有没有更好的方法来构建一个通过串行发送的数据包,而不是这样做:

unsigned char buff[255];

buff[0] = 0x02
buff[1] = 0x01
buff[2] = 0x03

WriteFile(.., buff,3, &dwBytesWrite,..);

注意:我有大约20个要发送的命令,所以如果有更好的方法以更简洁的方式将这些字节发送到串行设备而不必指定每个字节,那么太棒了每个字节都是十六进制的,最后一个字节是校验和。我应该澄清,我知道我必须指定每个字节来构建命令,但有没有比指定每个数组位置更好的方法?

6 个答案:

答案 0 :(得分:8)

您可以像这样初始化静态缓冲区:

const unsigned char command[] = {0x13, 0x37, 0xf0, 0x0d};

您甚至可以使用这些来初始化非常量缓冲区,然后仅用索引替换更改的字节。

答案 1 :(得分:8)

不确定你在问什么。如果您询问逐个设置字节并弄乱数据的问题,通常这是使用带有有意义名称的成员的压缩结构。像:

#pragma push(pack)
#pragma pack(1)

struct FooHeader {
  uint someField;
  byte someFlag;
  dword someStatus;
};

#pragma pack(pop)

FooHeader hdr;
hdr.someField = 2;
hdr.someFlag = 3;
hdr.someStatus = 4;

WriteFile(..., sizeof(hdr), &hdr);

答案 2 :(得分:2)

  

是否有更好的方法来构建数据包而不是逐字节组装?

是的,但需要一些思考和一些仔细的工程。许多其他答案告诉您其他机制,通过它们可以将C ++中的字节序列组合在一起。但我建议你设计一个表示数据包一部分的抽象:

class PacketField {
    void add_to_packet(Packet p);
};

然后你可以定义各种子类:

  • 向数据包添加单个字节
  • 以big-endian顺序添加一个16位整数。另一个小端。除了16之外的其他宽度。
  • 在数据包中添加一个字符串;通过插入长度然后插入字节来编码字符串。

您还可以定义更高阶的版本:

PacketField sequence(PacketField first, PacketField second);

返回一个按顺序包含两个参数的字段。如果您希望运算符重载,可以将其重载为+<<

您的基础Packet抽象只是一个可扩展的字节序列(动态数组),带有某种write方法。

如果您最终编写了许多网络协议,您会发现这种设计可以节省大量时间。

修改PacketField类的重点是可组合性和重用:

  • 通过编写数据包字段,您可以创建更复杂的数据包字段。例如,您可以将“添加TCP标头”定义为从PacketFieldsPacketFields的函数。

  • 幸运的是,您构建了一个PacketFields库,该库专用于您的应用程序或协议族或其他任何内容。然后重复使用库中的字段。

  • 您可以创建带有额外参数的PacketField子类。

很可能你可以做一些同样好的事情,而不必拥有这种额外的间接水平;我推荐它,因为我已经看到它在其他应用程序中有效使用。您正在通过实际构建特定数据包的行为来解耦如何构建数据包(可以随时应用于任何数据包)的知识。分离这样的问题可以帮助重用。

答案 3 :(得分:1)

是的,有一种更好的方法。让您的类读取和写入打包缓冲区。您甚至可以将其实现为接口。模板会有所帮助。

写作的一个例子:

template <typename Member_Type>
void Store_Value_In_Buffer(const Member_Type&, member,
                           unsigned char *& p_buffer)
{
  *((Member_Type *)(p_buffer)) = member;
  p_buffer += sizeof(Member_Type);
  return;
}

struct My_Class
{
    unsigned int datum;

    void store_to_buffer(unsigned char *& p_buffer)
    {
      Store_Value_In_Buffer(datum, buffer);
      return;
    }
};

//...
unsigned char    buffer[256];
unsigned char *  p_buffer(buffer);
MyClass object;
object.datum = 5;
object.store_to_buffer(p_buffer);
std::cout.write(p_buffer, 256);

接口的一部分也是查询对象的大小,它们将占用缓冲区,比如方法size_in_buffer。这留给读者练习。 : - )

答案 4 :(得分:1)

有一种更好的方法,即使用结构来设置结构。这通常是网络数据包在低级别构建的方式。

例如,假设您有包含id,length,flag byte和data的数据包,您可以执行以下操作:

struct packet_header {
   int id;
   byte length;
   byte flags;
}; 

byte my_packet[] = new byte[100];
packet_header *header = &my_packet;
header->id = 20;
header->length = 10; // This can be set automatically by a function, maybe?
// etc.
header++; // Header now points to the data section.

请注意,您必须确保结构“打包”,即当您编写byte length时,它确实需要一个字节。通常,您可以使用#pragma pack或类似的东西来实现这一点(您必须阅读有关编译器的编译指示设置)。

另外,请注意您应该使用函数来执行常见操作。例如,创建一个函数,它将大小,要发送的数据和其他信息作为输入,并为您填写数据包标题和数据。这样,您可以在长度字段中执行要写入的实际大小的计算,可以计算函数内部的CRC等。

编辑:这是一种以C为中心的做事方式,这是很多网络代码的风格。更多以C ++为中心(面向对象)的方法也可以工作,但我对它们不太熟悉。

答案 5 :(得分:0)

const char * c =“\ x02 \ x02 \ x03”;