构建数据包的更好方法 - 逐字节?
这与我今天在此处提出的问题有关所以。有没有更好的方法来构建通过串行发送的数据包,而不是这样做:
unsigned char buff[255];
buff[0] = 0x02
buff[1] = 0x01
buff[2] = 0x03
WriteFile(.., buff,3, &dwBytesWrite,..);
注意:我有大约二十个命令要发送,所以是否有更好的方法将这些字节发送到串行设备以更简洁的方式而不是必须指定每个字节,那就太好了。每个字节都是十六进制,最后一个字节是校验和。我应该澄清一下,我知道我必须指定每个字节来构建命令,但是有没有比指定每个数组位置更好的方法?
This is related to my question asked here today on SO. Is there a better way to build a packet to send over serial rather than doing this:
unsigned char buff[255];
buff[0] = 0x02
buff[1] = 0x01
buff[2] = 0x03
WriteFile(.., buff,3, &dwBytesWrite,..);
Note: I have about twenty commands to send, so if there was a better way to send these bytes to the serial device in a more concise manner rather than having to specify each byte, it would be great. Each byte is hexadecimal, with the last byte being the checksum. I should clarify that I know I will have to specify each byte to build the commands, but is there a better way than having to specify each array position?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可以像这样初始化静态缓冲区:
您甚至可以使用它们来初始化非常量缓冲区,然后按索引仅替换更改的字节。
You can initialize static buffers like so:
You could even use these to initialize non-const buffers and then replace only changing bytes by index.
不确定你在问什么。如果您询问逐一设置字节并弄乱数据的问题,通常这是通过包含具有有意义名称的成员的打包结构来完成的。喜欢:
Not sure what you're asking. If you ask about the problem of setting the byte one by one and messing up the data, usually this is doen with a packed struct with members having meaningful names. Like:
是的,但这需要一些思考和精心的设计。许多其他答案告诉您其他机制,您可以通过这些机制将 C++ 中的字节序列组合在一起。但我建议您设计一个代表数据包一部分的抽象:
然后您可以定义各种子类:
您还可以定义一个高阶版本:
返回一个按顺序包含两个参数的字段。如果您喜欢运算符重载,可以将其重载为
+
或<<
。您的底层
Packet
抽象只是一个可扩展的字节序列(动态数组),具有某种write
方法。如果您最终编写了许多网络协议,您会发现这种设计会带来巨大的回报。
编辑:
PacketField
类的要点是可组合性和重用性:通过组合数据包字段,您可以创建更复杂的数据包字段。例如,您可以将“添加 TCP 标头”定义为从
PacketFields
到PacketFields
的函数。如果幸运的话,您可以构建一个特定于您的应用程序或协议系列或其他内容的
PacketFields
库。然后您可以重用库中的字段。您可以创建采用额外参数的
PacketField
子类。很可能您可以做同样好的事情,而不必具有这种额外的间接级别;我推荐它是因为我已经看到它在其他应用程序中得到了有效的使用。您正在将如何构建数据包的知识(可以随时应用于任何数据包)与实际构建特定数据包的行为分开。像这样分离关注点可以帮助重用。
Yes, but it will require some thought and some careful engineering. Many of the other answers tell you other mechanisms by which you can put together a sequence of bytes in C++. But I suggest you design an abstraction that represents a part of a packet:
Then you can define various subclasses:
You also can define a higher-order version:
Returns a field that consists of the two arguments in sequence. If you like operator overloading you could overload this as
+
or<<
.Your underlying
Packet
abstraction will just be an extensible sequence of bytes (dynamic array) with some kind ofwrite
method.If you wind up programming a lot of network protocols, you'll find this sort of design pays off big time.
Edit: The point of the
PacketField
class is composability and reuse:By composing packet fields you can create more complex packet fields. For example, you could define "add a TCP header" as a function from
PacketFields
toPacketFields
.With luck you build up a library of
PacketFields
that are specific to your application or protocol family or whatever. Then you reuse the fields in the library.You can create subclasses of
PacketField
that take extra parameters.It's quite possibly that you can do something equally nice without having to have this extra level of indirection; I'm recommending it because I've seen it used effectively in other applications. You are decoupling the knowledge of how to build a packet (which can be applied to any packet, any time) from the act of actually building a particular packet. Separating concerns like this can help reuse.
是的,有更好的方法。让您的类读取和写入打包缓冲区。您甚至可以将其实现为接口。模板会有所帮助。
编写示例:
接口的一部分还用于查询对象在缓冲区中占用的大小,例如方法
size_in_buffer
。这留给读者作为练习。 :-)Yes, there is a better method. Have your classes read from and write to a packed buffer. You could even implement this as an interface. Templates would help to.
An example of writing:
Part of the interface is also to query the objects for the size that they would occupy in the buffer, say a method
size_in_buffer
. This is left as an exercise for the reader. :-)有一种更好的方法,那就是使用结构体来设置结构体。这通常是网络数据包在低级别上构建的方式。
例如,假设您有包含 id、长度、标志字节和数据的数据包,您将执行以下操作:
请注意,您必须确保结构已“打包”,即当你写
字节长度
,它确实占用了一个字节。通常,您可以使用诸如#pragma pack 或类似的东西来实现这一点(您必须阅读编译器的编译指示设置)。另请注意,您可能应该使用函数来执行常见操作。例如,创建一个函数,获取大小、要发送的数据和其他信息作为输入,并为您填写数据包标头和数据。这样,您可以计算要在长度字段中写入的实际大小,可以计算函数内部的 CRC,等等。
编辑:这是一种以 C 为中心的做事方式,这是很多网络代码的风格。更以 C++ 为中心(面向对象)的方法也可以工作,但我对它们不太熟悉。
There is a much better way, which is using structs to set the structures. This is usually how network packets are built on a low level.
For example, say you have packets which have an id, length, flag byte, and data, you'd do something like this:
Do note that you're going to have to make sure that the structures are "packed", i.e. when you write
byte length
, it really takes up a byte. Usually, you'd achieve this using something like#pragma pack
or similar (you'll have to read about your compiler's pragma settings).Also, note that you should probably use functions to do common operations. For example, create a function which gets as input the size, data to send, and other information, and fills out the packet header and data for you. This way, you can perform calculations about the actual size you want to write in the length field, you can calculate the CRC inside the function, etc.
Edit: This is a C-centric way of doing things, which is the style of a lot of networking code. A more C++-centric (object oriented) approach could also work, but I'm less familiar with them.
const char *c = "\x02\x02\x03";
const char *c = "\x02\x02\x03";