我想知道是否有任何已知的解析混合类型数据包的最佳实践/方法。
例如,假设数据是10个字节,它包括:
字节0-1:制造商ID (int)
字节2:类型(int)
字节3-4:设备id (ascii char)
我可以简单地将每个数据类型的大小和位置定义为# defines,并使用这些定义来解析它。但我想知道是否有更好的组织结构。
发布于 2018-11-10 07:21:24
最佳实践是假设来自程序外部的所有数据(例如来自用户、文件、网络、来自不同进程的数据)可能是不正确的(并且可能不安全/恶意)。
然后,在“潜在不正确”假设的基础上,定义了区分“未检查、潜在错误数据”和“已检查、已知正确数据”的类型。对于您的示例,您可以使用uint8_t packet[10];作为未检查数据的数据类型,并对所检查的数据使用普通结构(带填充和不带__attribute__((packed));)。这使得程序员在认为自己使用的是安全/检查数据时,很难意外地使用不安全的数据。
当然,您还需要代码来在这些数据类型之间进行转换,这些数据类型需要尽可能多地进行理智检查(可能还会担心诸如endianess)。例如,这些检查可以是:
请注意,此函数应返回某种状态以指示转换是否成功,而且在大多数情况下,此状态还应指示如果转换不成功会发生什么问题(以便调用方能够通知用户或记录问题或以最适合问题的方式处理问题)。例如,“未知制造商ID”可能意味着程序需要更新以处理新制造商,而且数据是正确的,“无效制造商ID”意味着数据肯定是错误的。
发布于 2018-11-10 04:54:19
如下所示:
struct packet {
uint16_t mfg;
uint8_t type;
uint16_t devid;
} __attribute__((packed));为了避免协议中不存在的隐式填充,需要填充属性(或平台的等效属性)。
一旦您拥有了上面的结构,您只需将从何处接收到的char数组转换(部分):
char buf[1000];
(struct packet*)(buf + N);发布于 2019-02-21 18:37:58
对于一个完全可移植的版本,我建议您以这种方式阅读:
struct {
uint16_t e1;
uint8_t e2;
uint16_t e3;
} d;
uint8_t *cursor;
uint8_t rbuf[5];
read(sock, rbuf, sizeof(rbuf));
memcpy(&s.e1, &rbuf[0], sizeof(s.e1));
s.e2 = rbuf[2];
memcpy(&s.e3, &rbuf[3], sizeof(s.e3));
s.e1 = ntohs(s.e1);
s.e3 = ntohs(s.e3);你可能会像别人说的那样做一些事情,比如:
struct s {
uint16_t e1;
uint8_t e2;
uint16_t e3;
} __attribute__((packed));
struct s d;
read(sock, &d, sizeof(d));
s.e1 = ntohs(s.e1);
s.e3 = ntohs(s.e3);但是,这段代码并不是完全可移植的,可能会导致问题,因为您正在使用未对齐内存访问项(s.e3),这本身就是未定义的行为。在某些情况下,这种方式是OK的,也是可取的(由于更多的结构可以填充不同的缓存行,或者更简单的代码),所以缓存污染较少,但在其他情况下,它可能导致总线错误,并使代码与某些体系结构不兼容。
除此之外,您还应该遵循其他最佳实践,比如尝试在read()调用之间读取尽可能多的结构,编写更好的关于网络到主机字节排序翻译的代码.但我认为,首先要避免非标准属性。
请注意,如果您不执行非对齐访问,所有这些(甚至是__packed__属性)都是完全不必要的,您可以读取如下结构:
struct {
uint16_t e1;
uint8_t e2;
uint8_t e2;
uint16_t e3;
} d;
read(rsock, &d, sizeof(d));https://stackoverflow.com/questions/53236024
复制相似问题