全部例程

W55MH32使用DNS解析域名

W55MH32

更新于 2026年9月14日

本篇总结

本例程在 W55MH32 上通过 DNS 协议解析域名获取服务器 IP 地址,为后续 TCP/UDP 通信提供目标地址。

🔑 KEIL MDK、UART调试、硬件TOE引擎、DNS、DHCP

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示如何在 W55MH32 上使用 DNS(域名系统)协议将域名解析为 IP 地址。通过定时器中断提供 DNS 协议栈所需的时基,调用 DNS_run() 函数向 DHCP 获取的 DNS 服务器发起查询请求,将 wiznet.io 等域名解析为对应的 IP 地址。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • WIZ UartTool V1.0:串口调试工具:点击下载
  • 网络抓包工具 Wireshark(可选):用于分析 DNS 报文:点击下载

DNS 协议简介

DNS(Domain Name System,域名系统)是互联网的一项核心服务,作为将 域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网,而不用去记住能够被机器直接读取的 IP 数串。例如,用户在浏览器中输入 wiznet.io,DNS 服务负责将其解析为对应的 IP 地址(如 183.111.138.249),使浏览器能够建立连接。

DNS 协议运行在 UDP 协议之上,使用 53 端口进行通信。DNS 报文主要由以下几部分组成:

字段说明
Header(头部)包含事务 ID、标志位、问题数、回答数等
Question(查询问题)包含要查询的域名和记录类型(如 A 记录)
Answer(回答)服务器返回的解析结果,包含域名对应的 IP
Authority(授权)授权域名服务器信息
Additional(附加)附加的记录信息

DNS 查询报文实例(16 进制):

8D 12 01 00 00 01 00 00 00 00 00 00
06 77 69 7A 6E 65 74 02 69 6F 00 00 01 00 01

其中 0x8D12 为事务 ID,0x0100 为标志位(标准查询),随后是要查询的域名 wiznet.io(按标签长度编码),最后 0x0001 0x0001 表示查询 A 记录(IPv4 地址)。

DNS 记录类型

本例程使用 A 记录查询(类型 0x0001),即将域名解析为 IPv4 地址。常见的记录类型还有 CNAME(别名记录)、AAAA(IPv6 地址)、MX(邮件交换)等。DNS 采用层次化结构,根域名服务器 → 顶级域名服务器(.com)→ 权威域名服务器,逐级完成解析。

硬件连接与拓扑

请注意:

测试本例程需要 PC 端和 W55MH32 处于同一网段,PC 和开发板需接入同一路由器或交换机,且路由器需能正常访问互联网(DNS 查询需转发至公网 DNS 服务器)。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器/交换机 LAN 口,路由器连接互联网
┌───────────┐ ┌─────────────────┐ ┌──────────────────┐
│ W55MH32 │──网线──│ 路由器/网关 │──互联网──│ DNS 服务器 │
│ (DNS │ │ (DHCP/DNS代理) │ │ 192.168.1.1 / │
│ Client) │ │ 192.168.1.1 │ │ 183.111.138.249 │
└───────────┘ └─────────────────┘ └──────────────────┘
↑ UDP:53
DEBUG USB ──→ WIZ UartTool
网络拓扑:W55MH32 通过路由器向 DNS 服务器(UDP 53 端口)发起域名查询请求

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\WZTOE\DNS\,核心文件如下:

DNS/
├── Project/
│   └── Project.uvprojx       ← Keil 工程文件
└── USER/
    ├── main.c                ← 硬件初始化 + do_dns() 调用
    ├── do_dns/
    │   ├── do_dns.c          ← DNS 解析主流程(do_dns)
    │   └── do_dns.h
    ├── wiz_interface/
    │   ├── wiz_interface.c   ← PHY 检测 / 网络信息打印
    │   └── wiz_interface.h
    ├── tim/
    │   ├── bsp_tim.c         ← TIM3 1ms 定时器(DNS/DHCP 时基)
    │   └── bsp_tim.h
    └── delay/
        └── delay.c/h          ← SysTick 毫秒延时

本例程相比 TOE 初始化章节的核心新增:TIM3 定时器中断(提供 DNS 和 DHCP 时基)和 DNS_run() 查询循环。

步骤1:注册 DNS 定时中断函数

文件:USER/tim/bsp_tim.c — 将 DNS_time_handler() 和 DHCP_time_handler() 注册到 1ms 定时器中断中,为 DNS 和 DHCP 协议栈提供时基。每 1000ms 执行一次时间处理。

void TIM3_IRQHandler(void)
{
    static uint32_t tim3_1ms_count = 0;
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
        tim3_1ms_count++;
        if (tim3_1ms_count >= 1000)
        {
            DHCP_time_handler();
            DNS_time_handler();
            tim3_1ms_count = 0;
        }
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

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 次数重试,超过则报错返回。

步骤3:主函数调用

文件:USER/main.c — 硬件初始化 → 配置网络(DHCP)→ 调用 do_dns() 一次 → 进入空循环等待。

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)

常见问题

Q: DNS_run() 返回 DNS_RET_FAIL 后会自动重试吗

会。在 do_dns() 内,DNS_run() 每次返回 DNS_RET_FAIL 后,会判断 dns_retry_cnt 是否小于 DNS_RETRY,未超过则继续循环调用。DNS 采用 UDP 通信,发送查询后需等待服务器响应,期间通过定时器中断更新超时计数器。

Q: 为什么必须配合 DNS_time_handler() 使用

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 地址,通过实战例程展示了完整的解析流程:

  • DNS 基础:DNS 运行在 UDP 53 端口,将域名映射为 IP 地址,采用层次化结构逐级解析
  • 定时器时基:通过 TIM3 中断每 1s 调用 DNS_time_handler() 和 DHCP_time_handler(),为协议栈提供超时计数
  • 查询流程:do_dns() 先 DNS_init() 初始化,再从网络信息中取 DNS 服务器地址,循环调用 DNS_run() 直到成功或超过重试次数
  • 报文结构:DNS 报文由 Header、Question、Answer 等部分组成,本例程使用 A 记录查询获取 IPv4 地址
  • 验证手段:配合 WIZ UartTool 观察解析结果,使用 Wireshark 抓包分析 DNS 报文

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家