全部例程

W6300使用DNS解析域名

W6300

更新于 2026年10月9日

本篇总结

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

🔑 MH2457、QSPI、UART调试、硬件TOE引擎、DNS、DHCP

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

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

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

前置工具准备

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

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

DNS 协议简介

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

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

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

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

11 23 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

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

DNS 记录类型

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

硬件连接与拓扑

请注意:

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

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器/交换机 LAN 口,路由器连接互联网
  3. 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
┌───────────┐ ┌─────────────────┐ ┌──────────────────┐
│ W6300 │──网线──│ 路由器/网关 │──互联网──│ DNS 服务器 │
│ (DNS │ │ (DHCP/DNS代理) │ │ 192.168.1.1 / │
│ Client) │ │ 192.168.1.1 │ │ 183.111.138.249 │
└────┬──────┘ └─────────────────┘ └──────────────────┘
│ QSPI UDP:53
┌────┴──────┐
│ MH2457 │──────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:W6300 通过路由器向 DNS 服务器(UDP 53 端口)发起域名查询请求,MH2457 通过 QSPI 驱动 W6300

工程结构

工程核心文件如下:

Project/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c/h         ← 网络地址定义 + 待解析域名 + user_run() 主流程
    ├── do_dns/
    │   ├── do_dns.c          ← DNS 解析主流程(do_dns)
    │   └── do_dns.h
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    ├── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器
    └── wizchip_conf.h        ← WIZnet 配置与 wiz_NetInfo 定义

本例程相比 TOE 初始化章节的核心新增:W6300 平台的软件定时器链表(通过 wiz_add_timer 为 DNS 和 DHCP 提供时基)和 DNS_run() 查询循环。

步骤1:注册 DNS 定时回调

文件:USER/wiz_interface.c — W6300 平台通过软件定时器链表为 DNS 提供秒级时基。wiz_timer_handler() 由 1ms 定时器中断调用,累加到注册的触发时间后执行回调函数。

void wiz_timer_handler(void)
{
    struct wiz_timer *temp;

    wiz_delay_ms_count++;

    temp = wiz_timer_head;
    while (temp != NULL)
    {
        temp->count_time++;

        if (temp->count_time >= temp->trigger_time)
        {
            temp->count_time = 0;
            temp->func();
        }

        temp = temp->next;
    }
}

DNS 协议栈通过 wiz_add_timer(DNS_time_handler, 1000) 将 DNS_time_handler() 注册到软件定时器链表,每秒触发一次,用于更新 DNS 内部超时计数器。该函数与 DNS_run() 配合使用,当 DNS 查询超时后会自动重试。DHCP 同理,通过 wiz_add_timer(DHCP_time_handler, 1000) 注册。

步骤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;
    uint8_t fallback_dns[4] = APP_DNS_SERVER;

    if ((buf == NULL) || (domain_name == NULL) || (domain_ip == NULL))
    {
        return -1;
    }

    wiz_add_timer(DNS_time_handler, 1000);
    DNS_init(DNS_SOCKET_ID, buf);
    memset(&net_info, 0, sizeof(net_info));
    wizchip_getnetinfo(&net_info);

    if (ipv4_is_zero(net_info.dns) != 0)
    {
        memcpy(net_info.dns, fallback_dns, sizeof(net_info.dns));
    }

    while (1)
    {
        switch (DNS_run(net_info.dns, domain_name, domain_ip))
        {
        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;

        default:
            break;
        }

        if (dns_run_flag != 1)
        {
            wiz_delete_timer(DNS_time_handler);
            return dns_ok_flag;
        }
    }
}

DNS_run() 返回值

DNS_RET_SUCCESS:解析成功,domain_ip 中存储解析到的 IP 地址;DNS_RET_FAIL:解析失败(域名无效或超时),未超过重试次数 DNS_RETRY 则继续重试。注意必须配合 wiz_add_timer(DNS_time_handler, 1000) 使用,否则 DNS_run() 会因超时计数器不更新而失败。

关键点:① 先通过 wiz_add_timer(DNS_time_handler, 1000) 注册秒级时基;② DNS_init(DNS_SOCKET_ID, buf) 初始化 DNS 客户端;③ DNS 服务器地址从 wizchip_getnetinfo() 获取,若为空则回退到 APP_DNS_SERVER(8.8.8.8);④ 失败后按 DNS_RETRY 次数重试,超过则报错返回;⑤ 结束时通过 wiz_delete_timer() 注销定时器回调。

步骤3:主函数调用

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

wiz_NetInfo default_net_info = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56},
    .ip = {192, 168, 1, 50},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 1, 1},
    .dns = {8, 8, 8, 8},
    .dhcp = NETINFO_DHCP
};

static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
uint8_t dns_name[] = "www.wiznet.io";
uint8_t ip_fromdns[4] = {0};

void user_run(void)
{
    printf("%s DNS example\r\n", _WIZCHIP_ID_);

    wizchip_initialize();
    network_init(ethernet_buf, &default_net_info);

    (void)do_dns(ethernet_buf, dns_name, ip_fromdns);

    while (1)
    {
    }
}

注意:do_dns() 只调用一次,内部已经通过 while 循环完成查询和重试,解析成功或失败后返回。返回后主程序进入空循环。待解析域名和结果 IP 缓冲区定义在 user_main.c 中。

运行验证

打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先进行 PHY 链路检测,然后通过 DHCP 获取网络参数(含 DNS 服务器地址),最后发起 DNS 查询将 www.wiznet.io 解析为 IP 地址。预期串口输出如下:

MH2457 W6300 QSPI example start
W6300 DNS example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.8
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================

> Translated www.wiznet.io to 183.111.138.249

解析成功后,串口会打印 > Translated www.wiznet.io to 183.111.138.249,表示已成功将域名解析为对应的 IP 地址。可使用 Wireshark 抓包工具在 PC 上捕获 UDP 53 端口的 DNS 报文,对照上文报文结构进行验证。

WIZ UartTool 串口输出 — PHY 链路检测 + DHCP 获取网络参数 + DNS 解析 www.wiznet.io 成功
Wireshark 抓包 — DNS 查询(Standard query A www.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() 在软件定时器中更新。W6300 平台通过 wiz_add_timer(DNS_time_handler, 1000) 注册,定时器由 1ms 中断驱动。wiz_timer_handler() 在系统定时中断中调用,累加到 1000ms 后触发 DNS_time_handler()。同理,DHCP 也需要通过 wiz_add_timer(DHCP_time_handler, 1000) 注册时基。如果未注册该函数,超时计数器不会递减,DNS_run() 可能无法正确判断超时,导致解析失败或卡死。

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(阿里)。本例程在 do_dns 中还提供了回退机制:若 DHCP 获取的 DNS 为空,则使用 APP_DNS_SERVER(8.8.8.8)。

Q: DNS 解析失败如何排查

首先确认网络连通性(能否 ping 通网关和 DNS 服务器);检查 DHCP 是否成功获取到 DNS 服务器地址(串口网络配置表格中的 DNS 字段);确认软件定时器已正确注册(wiz_add_timer(DNS_time_handler, 1000) 被调用);使用 Wireshark 抓包查看 DNS 查询报文是否发出及服务器是否响应;某些域名可能不存在 A 记录,可尝试更换其他域名测试。

总结

本文讲解了如何在 W6300 芯片上使用 DNS 协议将域名解析为 IP 地址,通过实战例程展示了完整的解析流程:

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

下载本章例程

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

开发环境: Keil MDK5

Andy

W5500 技术支持专家