【问题标题】:Deserializing a DNS packet object in C++11在 C++11 中反序列化 DNS 数据包对象
【发布时间】:2025-12-25 03:05:11
【问题描述】:

我有一个如下所示的 DNS 数据包类(我只粘贴了其中的一部分):

class DNSPacket {

public:
    struct DNSHeader {
        unsigned int ID :16; 
        unsigned int QR :1;  
        unsigned int OPCODE :4;  
        unsigned int AA :1;   
        unsigned int TC :1;  
        unsigned int RD :1;    
        unsigned int RA :1;   
        unsigned int Z :3;   

        unsigned int RCODE :4;
        unsigned int QDCOUNT :16;
        unsigned int ANCOUNT :16;
        unsigned int NSCOUNT :16;
        unsigned int ARCOUNT :16;
    };

private:
    DNSHeader header;
    std::vector<DNSQuestion> questions;
    std::vector<DNSAnswer> answers;
    std::vector<DNSAnswer> nameservers; // TODO: DNSAnswer?
    std::vector<DNSAnswer> add_records; // TODO: DNSAnswer?
}

char 数组反序列化为该对象的正确方法是什么?我有的选项是:重载&gt;&gt; 运算符,添加一个单独的类来反序列化它以读取和反序列化读取一个字节一个字节的数据并使用reinterpret_cast()

我想在 C++11 中创建一个快速、现代的实现。我应该选择哪种方式?另外,我应该如何反序列化位域 - 我应该坚持按位运算吗?

【问题讨论】:

  • char数组的格式是什么?
  • 这是一个封装了一些(问题或答案)DNS数据包的UDP数据帧。
  • 你的结构不对,根据thisZ字段是三位。
  • @JoachimPileborg 感谢您指出这一点,已修复。
  • 使用 96 位而不是之前的 94 位是有意义的。

标签: c++ c++11 serialization dns


【解决方案1】:

我这样做的方法是将字 2(第 16 位到第 31 位)视为单个无符号 16 位整数(例如 uint16_t),并通过使用按位 AND 和 SHIFT 操作简单地获取位。所有其他单词都可以以相同的方式读取,但或多或​​少按原样使用(当然是在从网络字节顺序转换为主机字节顺序之后)。

要将单词 X 单词作为 uint16_t,您必须进行一些转换:

uint16_t wordX = *reinterpret_cast<uint16_t*>(&your_array[X]);

请注意,对于第二个字,由于它们实际上只是按指定顺序排列的位,因此不会对它们进行字节顺序转换。

【讨论】:

  • 字节序呢? Thete 没有说明他正在开发哪个平台。
  • @marom 除非 RFC 另有说明,否则超过 8 位的字应始终按网络字节顺序排列,无论协议如何。
  • 这对于来自网络的数据来说是正确的,但现在大多数平台都是小端的,所以对于 16 位字应该有字节反转。 uint16_t wordX = (your_array[X]
  • @marom 你知道ntohs 函数吗?我的回答中确实有关于从网络字节顺序转换的说明。