DNS(Domain Name System,域名系统)是互联网的一项核心服务,作为将 域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,而不用去记住能够被机器直接读取的 IP 数串。例如,用户在浏览器中输入 wiznet.io,DNS 服务负责将其解析为对应的 IP 地址(如 183.111.138.249),使浏览器能够建立连接。
DNS_time_handler() 在 1s 周期内被调用一次,用于更新 DNS 内部超时计数器。该函数与 DNS_run() 配合使用,当 DNS 查询超时后会自动重试。同时 DHCP_time_handler() 也为 DHCP 提供超时计数。
步骤2:DNS 解析函数 do_dns()
文件:USER/do_dns/do_dns.c — 传入以太网缓冲区、待解析域名、结果 IP 缓冲区,先调用 DNS_init() 初始化 DNS 客户端,再用 wizchip_getnetinfo() 获取网络信息(含 DNS 服务器地址),最后循环调用 DNS_run() 发起查询,直到成功或超过重试次数。
int do_dns(uint8_t *buf, uint8_t *domain_name, uint8_t *domain_ip)
{
int dns_ok_flag = 0;
int dns_run_flag = 1;
wiz_NetInfo net_info;
uint8_t dns_retry_cnt = 0;
DNS_init(0, buf); // DNS 客户端初始化,socket 0
wizchip_getnetinfo(&net_info); // 获取网络信息(含 DNS 服务器地址)
while (1)
{
switch (DNS_run(net_info.dns, domain_name, domain_ip)) // 发起 DNS 查询
{
case DNS_RET_FAIL: // 解析失败
{
if (dns_retry_cnt < DNS_RETRY) // 未超过重试次数则继续
{
dns_retry_cnt++;
}
else
{
printf("> DNS Failed\r\n");
dns_ok_flag = -1;
dns_run_flag = 0;
}
break;
}
case DNS_RET_SUCCESS: // 解析成功
{
printf("> Translated %s to %d.%d.%d.%d\r\n",
domain_name, domain_ip[0], domain_ip[1], domain_ip[2], domain_ip[3]);
dns_ok_flag = 0;
dns_run_flag = 0;
break;
}
}
if (dns_run_flag != 1)
{
return dns_ok_flag;
}
}
}
DNS_run() 返回值
DNS_RET_SUCCESS:解析成功,domain_ip 中存储解析到的 IP 地址;DNS_RET_FAIL:解析失败(域名无效或超时),未超过重试次数 DNS_RETRY 则继续重试。注意必须配合定时器中断中的 DNS_time_handler() 使用,否则 DNS_run() 会因超时计数器不更新而失败。
关键点:① 先调用 DNS_init(0, buf) 初始化 DNS 客户端;② DNS 服务器地址从 wizchip_getnetinfo() 获取,无需硬编码;③ 失败后按 DNS_RETRY 次数重试,超过则报错返回。
uint8_t dns_name[] = "wiznet.io"; // 待解析的域名
uint8_t ip_fromdns[4]; // DNS 解析结果 IP
int main(void)
{
/* 硬件初始化 */
rcc_clk_config();
delay_init();
console_usart_init(115200);
tim3_init();
printf("%s DNS example\r\n", _WIZCHIP_ID_);
/* WIZTOE 初始化 */
wiz_toe_init();
#if DEFAULT_MAC_EN == 1
getSHAR(default_net_info.mac);
#endif
wiz_phy_link_check();
network_init(ethernet_buf, &default_net_info);
/* DNS 解析:只调用一次,解析完成后进入空循环 */
do_dns(ethernet_buf, dns_name, ip_fromdns);
while (1)
{
}
}
注意:do_dns()只调用一次,内部已经通过 while 循环完成查询和重试,解析成功或失败后返回。返回后主程序进入空循环。
运行验证
打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先进行 PHY 链路检测,然后通过 DHCP 获取网络参数(含 DNS 服务器地址),最后发起 DNS 查询将 wiznet.io 解析为 IP 地址。预期串口输出如下:
W55MH32 DNS example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
W55MH32 network configuration : DHCP
MAC : 00:08:DC:00:0E:9A
IP : 192.168.1.6
Subnet Mask : 255.255.255.0
Gateway : 192.168.1.1
DNS : 192.168.1.1
====================================================================================================
> Translated wiznet.io to 183.111.138.249
解析成功后,串口会打印 > Translated wiznet.io to 183.111.138.249,表示已成功将域名解析为对应的 IP 地址。可使用 Wireshark 抓包工具在 PC 上捕获 UDP 53 端口的 DNS 报文,对照上文报文结构进行验证。
WIZ UartTool 串口输出 — PHY 链路检测 + DHCP 获取网络参数 + DNS 解析 wiznet.io 成功Wireshark 抓包 — DNS 查询(Standard query A wiznet.io)与响应报文(Standard query response A 183.111.138.249)
DNS 协议栈内部维护超时计数器,用于判断查询是否超时并触发重试。该计数器由 DNS_time_handler() 在定时器中断中更新。如果未注册该函数,超时计数器不会递减,DNS_run() 可能无法正确判断超时,导致解析失败或卡死。同理,DHCP 也需要 DHCP_time_handler()。
Q: 如何更换 DNS 服务器
本例程使用 DHCP 自动获取的 DNS 服务器地址(通过 wizchip_getnetinfo() 读取)。如需手动指定,可在 wiz_NetInfo 中把 dhcp 改为 NETINFO_STATIC 并填入静态 DNS。常用公共 DNS 服务器有 114.114.114.114(国内)、8.8.8.8(Google)、223.5.5.5(阿里)。
Q: DNS 解析失败如何排查
首先确认网络连通性(能否 ping 通网关和 DNS 服务器);检查 DHCP 是否成功获取到 DNS 服务器地址(串口网络配置表格中的 DNS 字段);确认定时器中断已正确注册(DNS_time_handler 每 1s 调用一次);使用 Wireshark 抓包查看 DNS 查询报文是否发出及服务器是否响应;某些域名可能不存在 A 记录,可尝试更换其他域名测试。
总结
本文讲解了如何在 W55MH32 芯片上使用 DNS 协议将域名解析为 IP 地址,通过实战例程展示了完整的解析流程: