W6300 IPv6 UDP通信测试
更新于 2026年10月15日
本篇总结
使用 W6300 的硬件 TOE 引擎在 IPv6 环境下实现 UDP 数据回环测试,先通过无状态地址自动配置(SLAAC)获取 IPv6 地址,再进行 UDP 收发。
硬件平台:本例程基于 W6300-EVB-M4 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板
本示例演示使用 W6300 的硬件 TOE 引擎在 IPv6 环境下实现 UDP 数据回环测试。W6300 原生支持 IPv4/IPv6 双协议栈,本例程先通过 DAD 检测生成 Link-local 地址,再通过路由器公告(RA)进行无状态地址自动配置(SLAAC)获取 Global 地址,最后在主循环中运行 UDP 回环服务器(监听 5000),通过 recvfrom() / sendto() 收发数据。
前置工具准备
IPv6 UDP 通信简介
UDP(User Datagram Protocol)是一种无连接的、不可靠的传输层协议。与 TCP 不同,UDP 不需要建立连接,直接发送数据报文,具有开销小、延迟低的特点。UDP 不保证数据到达、不保证顺序、不保证完整性,但传输效率高,适合实时音视频、DNS 查询等对实时性要求高但对可靠性要求不严格的场景。
W6300 片内集成完整的硬件双协议栈 TCP/IP,支持 IPv4、IPv6 以及双栈模式,UDP Socket 可通过 Sn_MR_UDP4、Sn_MR_UDP6、Sn_MR_UDPD 选择协议栈模式。
- 无连接:无需三次握手,直接发送数据报文
- 不可靠:不保证数据到达,不保证顺序,无重传机制
- 高效率:无连接建立开销,头部仅 8 字节(TCP 为 20 字节)
- 支持广播/组播:可向多个目标同时发送数据
- recvfrom()/sendto():收发数据时自动获取/指定对端地址,IPv6 下地址长度为 16
SLAAC 无状态地址自动配置
SLAAC(Stateless Address Autoconfiguration)是 IPv6 中通过路由器公告(RA)自动配置地址的机制。路由器周期性发送 RA 报文,包含网络前缀、网关、DNS 等信息。设备根据 RA 中的前缀和自身的接口标识符组合生成 Global 地址。本例程通过 W6300 的 MACRAW 模式捕获 RA 报文并解析,完成地址自动配置。
硬件连接与拓扑
请注意:
测试需要路由器支持并开启 IPv6,且发送 RA 公告。PC 和开发板需在同一网段。PC 端运行 UDP 测试工具与开发板通信。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
│ 路由器(IPv6 RA / DHCPv6) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W6300 │ │ PC │
│ UDP Srv │ │SocketTester│
│ IPv6 SLAAC│ │ UDP Client│
│ port:5000 │ │ │
└────┬──────┘ └───────────┘
│ QSPI ↑
┌────┴──────┐ DEBUG USB
│ MH2457 │─────────→ WIZ UartTool
│ MCU │
└───────────┘
工程结构
工程核心文件如下:
Project/
├── main.c ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
├── user_main.c ← IPv6 自动配置 + UDP 回环主循环
├── ipv6_aac.c/h ← IPv6 无状态地址自动配置(SLAAC)
├── ipv6_common.c/h ← IPv6 地址准备与打印
├── loopback_compat.c/h ← IPv6 UDP 封装(ipv6_udp_run)
├── loopback.c/h ← loopback_udps() 状态机核心
├── wiz_platform.c/h ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
└── wiz_interface.c/h ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器
步骤1:IPv6 地址自动配置
文件:USER/ipv6_aac.c — 先通过 ipv6_prepare_network() 初始化网络参数,再调用 AddressAutoConfig_Init() 执行 DAD 检测和 RA 解析,获取 Link-local 和 Global 地址。
if (ipv6_prepare_network(&net_info, mac, NETINFO_SLAAC_V6, NETINFO_STATIC) < 0)
{
return -1;
}
/* AddressAutoConfig may enter DHCPv6 paths depending on RA M/O flags. */
wiz_add_timer(DHCP6_time_handler, 1000U);
result = AddressAutoConfig_Init(IPV6_AAC_SOCKET, &net_info);
wiz_delete_timer(DHCP6_time_handler);
if (result != 1U)
{
return -1;
}
wizchip_getnetinfo(&net_info);
if (ipv6_address_is_zero(net_info.lla) || ipv6_address_is_zero(net_info.gua))
{
return -1;
}
ipv6_print_address("Link-local address: ", net_info.lla);
ipv6_print_address("Global address: ", net_info.gua);
if (!ipv6_address_is_zero(net_info.gw6))
{
ipv6_print_address("IPv6 gateway: ", net_info.gw6);
}
if (!ipv6_address_is_zero(net_info.dns6))
{
ipv6_print_address("IPv6 DNS: ", net_info.dns6);
}软件定时器
W6300 的 DHCPv6 时基通过 wiz_add_timer(DHCP6_time_handler, 1000) 注册到软件定时器链表,由 1ms 中断调用 wiz_timer_handler() 驱动。地址自动配置完成后调用 wiz_delete_timer() 删除定时器。
步骤2:主循环运行 UDP 回环测试
文件:USER/user_main.c — 在主循环中调用 ipv6_udp_run(),设置 IPv6 模式后运行 UDP 回环状态机。
while (1)
{
(void)ipv6_udp_run(UDP_SOCKET_ID, ethernet_buf, local_port);
}
三个传参分别为:SOCKET ID、UDP 协议缓存数组、本地监听端口号。示例本地端口为 5000。主循环不断调用状态机函数,等待 UDP 数据报文到达并回环发送。
int32_t ipv6_udp_run(uint8_t sn, uint8_t *buf, uint16_t port)
{
if (set_loopback_mode_W6x00(AS_IPV6) != 0)
{
return -1;
}
return loopback_udps(sn, buf, port);
}步骤3:UDP 状态机
loopback_udps() 函数基于 getsockopt(sn, SO_STATUS, &status) 返回的 Socket 状态执行对应操作。UDP 模式状态简单,只有 SOCK_CLOSED 和 SOCK_UDP 两个状态:
| Socket状态 | 执行操作 |
|---|---|
| SOCK_CLOSED | 打开 UDP Socket(按协议栈模式选择 UDP4/UDP6/UDPD),绑定端口,进入 SOCK_UDP |
| SOCK_UDP | recvfrom() 接收数据并获取对端地址,sendto() 回环发送 |
文件:USER/loopback.c | 函数:loopback_udps()
int32_t loopback_udps(uint8_t sn, uint8_t* buf, uint16_t port)
{
check_loopback_mode_W6x00();
uint8_t status;
static uint8_t destip[16] = {0,};
static uint16_t destport;
uint8_t addr_len;
int32_t ret;
uint16_t received_size;
uint16_t sentsize;
uint8_t* mode_msg;
if (loopback_mode == AS_IPV4)
mode_msg = msg_v4;
else if (loopback_mode == AS_IPV6)
mode_msg = msg_v6;
else
mode_msg = msg_dual;
getsockopt(sn, SO_STATUS, &status);
switch (status)
{
case SOCK_UDP:
getsockopt(sn, SO_RECVBUF, &received_size);
if (received_size > DATA_BUF_SIZE)
received_size = DATA_BUF_SIZE;
if (received_size > 0)
{
ret = recvfrom(sn, buf, received_size, (uint8_t*)&destip, (uint16_t*)&destport, &addr_len);
if (ret <= 0) return ret;
received_size = (uint16_t)ret;
sentsize = 0;
while (sentsize != received_size)
{
ret = sendto(sn, buf + sentsize, received_size - sentsize, destip, destport, addr_len);
if (ret < 0) return ret;
sentsize += ret;
}
}
break;
case SOCK_CLOSED:
switch (loopback_mode)
{
case AS_IPV4: socket(sn, Sn_MR_UDP4, port, SOCK_IO_NONBLOCK); break;
case AS_IPV6: socket(sn, Sn_MR_UDP6, port, SOCK_IO_NONBLOCK); break;
case AS_IPDUAL: socket(sn, Sn_MR_UDPD, port, SOCK_IO_NONBLOCK); break;
}
printf("%d:Opened, UDP loopback, port [%d] as %s\r\n", sn, port, mode_msg);
break;
default:
break;
}
return 0;
}
状态机核心:SOCK_CLOSED 时按当前协议栈模式调用 socket(sn, Sn_MR_UDP6, port, SOCK_IO_NONBLOCK) 打开 UDP Socket 并绑定端口;SOCK_UDP 时调用 recvfrom() 接收数据并自动获取对端 IP 和端口,然后 sendto() 将数据回环发送给对端。UDP 无需建立连接,状态机比 TCP 简单得多。
recvfrom() / sendto() — UDP 收发 API
recvfrom(sn, buf, size, destip, &destport, &addr_len) 接收 UDP 数据报文,同时将发送方的 IP 地址、端口号以及地址长度(4 表示 IPv4,16 表示 IPv6)写入对应参数。sendto(sn, buf, size, destip, destport, addr_len) 向指定 IP 和端口发送 UDP 数据。这两个函数是 UDP 编程的核心——每次收发都携带对端地址,无需维护连接状态。
如果你想自定义
- 改监听端口 → 修改
user_main.c中local_port的值 - 改用 IPv4 或双栈模式 → 调用
set_loopback_mode_W6x00(AS_IPV4)或AS_IPDUAL - 主动发送 UDP 数据 → 在 SOCK_UDP 状态中直接调用
sendto(),指定目标 IP 和端口 - 改用其他 Socket 编号 → 修改
UDP_SOCKET_ID宏(W6300 支持 0-7 共 8 个 Socket)
运行验证
打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先进行 PHY 链路检测,然后执行 DAD 检测和 RA 解析,完成 IPv6 地址自动配置后打开 UDP Socket 并监听 5000 端口。预期串口输出如下:
MH2457 W6300 QSPI example start
W6300 IPv6 UDP example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
Duplicate_Address_Detection
Wait RQIR.....
Timeout !!! DAD SUCCESSED
flags = 0x80
Address_Auto_Configuration Start
RA capture mode : W6300 MACRAW
Sn_SR : 42
RA
type : 86
code : 0
RA_flag : c0
Router_lifetime : 1800 s
Reachable_time : 0 ms
Retrans_time : 0 ms
Option Type : 1, length : 8
Option Type : 5, length : 8
Option Type : 3, length : 32
Prefix Length : 64
Prefix Information Flag : c0
valid lifetime : 234349
preferred lifetime : 147949
Accepted first autonomous /64 PIO
Option Type : 25, length : 24
DNS lifetime : 1800
Option Type : 7, length : 8
MO normalized result : 3
RA requests Stateful DHCPv6
Mac address : 00:08:dc:12:34:56
your Link Local IP is fe80:0000:0000:0000:0208:dcff:fe12:3456
your Global IP is 240e:033d:945a:a301:0208:dcff:fe12:3456
your Gateway IP is fe80:0000:0000:0000:0000:0000:0000:0002
your DNSv6 is 240e:033d:945a:a301:0000:0000:0000:0001
Stateful DHCPv6 succeeded
Link-local address: FE80:0000:0000:0000:0208:DCFF:FE12:3456
Global address: 240E:033D:945A:A301:0000:0000:0000:05EC
IPv6 gateway: FE80:0000:0000:0000:0000:0000:0000:0002
IPv6 DNS: 240E:033D:945A:A301:0000:0000:0000:0001
IPv6 auto configuration complete; starting application loop
1:Opened, UDP loopback, port [5000] as IPv6 mode
串口输出依次显示 DAD 检测成功、RA 报文解析(包含前缀、DNS、网关等选项)、IPv6 地址自动配置完成,最后 UDP Socket 在 IPv6 模式下打开并监听 5000 端口。


常见问题
Q: 为什么 IPv6 要先做 DAD 检测
IPv6 设备在链路上通信前必须先拥有一个唯一的 Link-local 地址。DAD(重复地址检测)通过发送邻居请求确认该地址没有被其他设备占用,确保地址唯一后才能继续获取 Global 地址。串口打印 "DAD SUCCESSED" 表示地址可用。
Q: UDP和TCP有什么区别
UDP 无连接、不可靠但高效,无需三次握手,头部仅 8 字节。TCP 面向连接、可靠传输,通过三次握手建立连接,头部 20 字节。UDP 适合实时音视频、DNS 查询等场景,TCP 适合文件传输、网页浏览等场景。
Q: recvfrom和recv有什么区别
recvfrom() 用于 UDP,接收数据同时获取发送方 IP、端口和地址长度(通过 destip、destport 和 addr_len 参数)。recv() 用于 TCP,已建立连接无需获取对端地址。IPv6 下地址长度为 16,IPv4 下为 4。
Q: UDP支持广播吗
支持。将 sendto() 的目标 IP 设为 255.255.255.255(全局广播)或子网广播地址(如 192.168.1.255),即可向网络中所有设备发送 UDP 广播报文。IPv6 下没有广播概念,对应的功能由组播实现。广播常用于设备发现、服务通告等场景。
Q: IPv6 地址自动配置失败怎么办
排查步骤:1) 确认路由器支持并开启 IPv6,且发送 RA 公告;2) 确认 PC 和 W6300 在同一网段;3) 检查串口是否打印 DAD SUCCESSED 和 RA 解析信息;4) 若 RA 未收到,检查路由器 IPv6 配置;5) 程序在自动配置失败时不会启动应用循环,可根据串口提示定位问题。
总结
本文基于 W6300 硬件 TCP/IP 协议栈实现了 IPv6 环境下的 UDP 数据回环测试:
- 地址获取:先通过 DAD 生成 Link-local 地址,再通过 RA 解析完成 SLAAC 无状态地址自动配置,必要时进入 DHCPv6
- UDP 协议:无连接、不可靠但高效,头部仅 8 字节,适合实时性要求高的场景
- 核心函数:loopback_udps() 基于 Socket 状态机,SOCK_CLOSED → SOCK_UDP 两状态切换
- 收发 API:recvfrom() 接收数据并获取对端地址,sendto() 向指定地址发送数据,IPv6 下地址长度为 16
- 双协议栈:W6300 支持 IPv4 / IPv6 / 双栈模式,通过 socket() 的 Sn_MR_UDP4/UDP6/UDPD 选择