【C/C++】用 C 语言手写 DNS 查询:理解 UDP 和协议报文

目录

  • [用 C 语言手写 DNS 查询:理解 UDP 和协议报文](#用 C 语言手写 DNS 查询:理解 UDP 和协议报文)

用 C 语言手写 DNS 查询:理解 UDP 和协议报文

学习代码:dns/dns.c

DNS 项目让我第一次比较具体地感受到:网络编程不只是 socketsendtorecvfrom,更重要的是理解协议格式。UDP 本身只负责把一段数据发给对方,至于这段数据里每个字节是什么意思,要由应用层协议决定。DNS 查询就是一个很好的练习,因为它的头部固定,问题区可变,既有结构又不会过分复杂。

项目里定义了 DNS 头部:

c 复制代码
struct dns_header
{
    uint16_t id;
    uint16_t flags;
    uint16_t questions;
    uint16_t answer;
    uint16_t authority;
    uint16_t additional;
};

这 12 个字节里,id 用来匹配请求和响应,flags 表示查询标志,questions 表示问题数量。写 DNS 请求时不能只把结构体随便发出去,还要注意网络字节序。多字节字段在网络上传输通常使用大端序,因此构造报文时需要 htons() 之类的转换。

DNS 问题区最有意思的是域名编码。普通字符串是 www.baidu.com,但 DNS 报文里不是直接这样放,而是按"长度 + 内容"的形式编码:

text 复制代码
3www5baidu3com0

也就是说,每一段标签前面放一个长度字节,最后用 0 结束。这个格式一开始不直观,但它让解析方不需要扫描点号,只要读长度就知道下一段有多长。

UDP 部分的流程相对直接:

c 复制代码
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sendto(sockfd, request, request_len, 0,
       (struct sockaddr *)&server_addr, sizeof(server_addr));
recvfrom(sockfd, response, sizeof(response), 0, NULL, NULL);

我的理解是,UDP 的"简单"并不等于应用简单。它没有 TCP 的连接管理和可靠传输,所以速度快、开销低,适合 DNS 这种一次请求一次响应的场景。但也正因为它不保证可靠,应用层要能处理超时、丢包、响应解析失败等情况。

这个项目最大的收获是:学习网络协议要从字节层面理解。只会调用 API,遇到问题时容易停在表面;知道报文结构,才能解释为什么一个域名要这样编码,为什么响应里可能有压缩指针,为什么同样是网络通信,DNS 和 HTTP 的处理方式完全不同。

学习链接: https://github.com/0voice