使用NetBIOS名称ping W55MH32
更新于 2026年9月17日
本篇总结
本例程在 W55MH32 上实现 NetBIOS 名称解析功能,使 PC 端可直接通过 NetBIOS 名称 ping 设备。
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示如何在 W55MH32 上实现 NetBIOS(网络基本输入输出系统)功能,通过名称进行 PING 测试。设备启用 DHCP 后 IP 地址会动态变化,NetBIOS 协议将固定名称映射到动态 IP,PC 端可直接通过名称 ping 设备,无需关心 IP 地址。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
NetBIOS 协议简介
NetBIOS(Network Basic Input/Output System,网络基本输入输出系统)由 IBM 在 1983 年开发,用于局域网通信,在局域网共享、网络管理等场景广泛应用。它为网络资源分配唯一名称(16 个字符),支持可靠的会话服务和快速的无连接数据报服务,计算机通过向名称服务器注册和查询,实现基于名称的通信。NetBIOS 工作在会话层,通过 UDP 137 端口进行名称服务,UDP 138 端口用于数据报服务,TCP 139 端口用于会话服务。
NetBIOS 报文主要字段说明:
| 字段 | 说明 |
|---|---|
| Transaction ID | 事务 ID,用于标识请求与响应的唯一事务,便于匹配查询和应答报文 |
| Flags | 标志位,指示报文类型(请求/响应),包含广播标志、操作码及控制信息 |
| Questions | 查询数量,表示当前查询的名称数量,通常为 1 |
| Answer RRs | 回答记录数,表示响应中返回的资源记录数 |
| Question Name | 查询名称,经过特殊编码的 NetBIOS 名称,占用 16 字节 |
| Question Type | 查询类型,0x20 表示 NetBIOS 名称查询 |
| Question Class | 查询类,0x01 表示 IN(互联网类查询) |
报文示例:PC 端发送的 NetBIOS 名称查询广播报文,请求解析设备名称。
|报文解析|
NetBIOS Name Service
Transaction ID: 0xa754 (唯一标识此查询,用于匹配请求与响应)
Flags: 0x0110, Opcode: Name query, Recursion desired, Broadcast (表示这是一个广播查询请求)
Questions: 1 (字段说明仅查询一个设备名称)
Answer RRs: 0 (在响应报文中,该字段会显示解析到的记录数)
Authority RRs: 0 (在响应报文中,用于指示哪些服务器可以授权回答该查询)
Additional RRs: 0 (在某些NetBIOS响应中可能用于携带更多解析信息)
|报文原文|
a7 54 01 10 00 01 00 00 00 00 00 00NetBIOS 的优势
在实际应用中,设备通常使用 DHCP 方式配置网络地址,IP 地址会动态变化。NetBIOS 协议将固定的名称映射到动态 IP,用户只需记住设备名称即可访问,无需每次查看 IP 地址。W55MH32 SDK 内置了完整的 NetBIOS 名称服务库(netbios.c/h),名称解码、报文组包、UDP 状态机全部由库函数处理。
硬件连接与拓扑
请注意:
PC 和开发板需接入同一路由器(同一网段),NetBIOS 基于 UDP 广播,广播报文只在同一网段内传播。路由器需开启 DHCP 服务。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
│ 路由器 │
│ 192.168.1.1 │
└────┬──────────────────┬──────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ PC │ │ W55MH32 │
│ CMD:ping │ │ NBNS:137 │
└───────────┘ └───────────┘
↑
DEBUG USB ──→ WIZ UartTool
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\WZTOE\5.NetBIOS\,核心文件如下:
5.NetBIOS/
├── Project/
│ └── Project.uvprojx ← Keil 工程文件
└── USER/
├── main.c ← 硬件初始化 + 网络配置 + NetBIOS 主循环
├── wiz_interface/
│ ├── wiz_interface.c ← PHY 检测 / 网络信息打印
│ └── wiz_interface.h
├── netbios/
│ ├── netbios.c ← NetBIOS 名称服务 (UDP 137)
│ └── netbios.h ← NetBIOS 名称/端口定义
├── dhcp/
│ ├── dhcp.c ← DHCP 客户端状态机 (DORA)
│ └── dhcp.h
├── uart/
│ ├── bsp_uart.c ← 调试串口 + printf 重定向
│ └── bsp_uart.h
├── tim/
│ └── bsp_tim.c/h ← TIM3 1ms 定时器(供 DHCP 超时)
└── delay/
└── delay.c/h ← SysTick 毫秒延时
本例程相比 DHCP 章节的核心新增:NetBIOS 名称服务的初始化、UDP socket 状态机运行、名称解码与响应报文构造。
步骤1:主循环调用 do_netbios
文件:USER/main.c — 在完成硬件初始化、DHCP 获取网络地址后,在主循环中调用 do_netbios() 函数,持续监听和响应 NetBIOS 名称查询请求。
while (1)
{
do_netbios(SOCKET_ID);
}
do_netbios() 函数需要传入 socket 号作为参数,设备会在该 socket 上监听 UDP 137 端口的 NetBIOS 名称查询报文。
步骤2:do_netbios 函数实现
文件:USER/netbios/netbios.c — do_netbios() 函数内部执行一个 UDP 协议的状态机。当收到消息后,首先判断是否为 NetBIOS 查询报文,如果匹配则解析名称并构造响应报文回复自身 IP 地址。
/**
*@brief Execute the NetBIOS name resolver
*@param socket number
*@return no
*/
void do_netbios(uint8_t sn)
{
unsigned char state;
unsigned int len;
state = getSn_SR(sn);
switch (state)
{
case SOCK_UDP:
if ((len = getSn_RX_RSR(sn)) > 0)
{
unsigned char rem_ip_addr[4];
uint16_t rem_udp_port;
char netbios_name[NETBIOS_NAME_LEN + 1];
NETBIOS_HDR *netbios_hdr;
NETBIOS_NAME_HDR *netbios_name_hdr;
len = recvfrom(sn, (unsigned char *)&netbios_rx_buf, len, rem_ip_addr, &rem_udp_port);
printf("rem_ip_addr=%d.%d.%d.%d:%d\r\n", rem_ip_addr[0], rem_ip_addr[1], rem_ip_addr[2], rem_ip_addr[3], rem_udp_port);
netbios_hdr = (NETBIOS_HDR *)netbios_rx_buf;
netbios_name_hdr = (NETBIOS_NAME_HDR *)(netbios_hdr + 1);
// If the packet is a NetBIOS query packet
if (((netbios_hdr->flags & ntohs(NETB_HFLAG_OPCODE)) == ntohs(NETB_HFLAG_OPCODE_NAME_QUERY)) && ((netbios_hdr->flags & ntohs(NETB_HFLAG_RESPONSE)) == 0) && (netbios_hdr->questions == ntohs(1)))
{
printf("netbios name query question\r\n");
// Decode the NetBIOS package
netbios_name_decoding((char *)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
printf("name is %s\r\n", netbios_name);
// If the query is made against the native Netbios
if (strcmp(netbios_name, NETBIOS_W55MH32_NAME) == 0)
{
uint8_t ip_addr[4];
NETBIOS_RESP *resp = (NETBIOS_RESP *)netbios_tx_buf;
// Handle the header of the NetBIOS response packet
resp->resp_hdr.trans_id = netbios_hdr->trans_id;
resp->resp_hdr.flags = htons(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE | NETB_HFLAG_RECURS_DESIRED);
resp->resp_hdr.questions = 0;
resp->resp_hdr.answerRRs = htons(1);
resp->resp_hdr.authorityRRs = 0;
resp->resp_hdr.additionalRRs = 0;
// Process the header data of the NetBIOS response packet
memcpy(resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
resp->resp_name.nametype = netbios_name_hdr->nametype;
resp->resp_name.type = netbios_name_hdr->type;
resp->resp_name.cls = netbios_name_hdr->cls;
resp->resp_name.ttl = htonl(NETBIOS_NAME_TTL);
resp->resp_name.datalen = htons(sizeof(resp->resp_name.flags) + sizeof(resp->resp_name.addr));
resp->resp_name.flags = htons(NETB_NFLAG_NODETYPE_BNODE);
getSIPR(ip_addr);
memcpy(resp->resp_name.addr, ip_addr, 4);
// Send a response packet
sendto(sn, (unsigned char *)resp, sizeof(NETBIOS_RESP), rem_ip_addr, rem_udp_port);
printf("send response\r\n");
}
}
}
break;
case SOCK_CLOSED:
close(sn);
socket(sn, Sn_MR_UDP, NETBIOS_PORT, 0);
break;
default:
break;
}
}状态机说明
SOCK_CLOSED 状态下初始化 UDP socket 并绑定 NETBIOS_PORT(137)端口;SOCK_UDP 状态下接收数据,判断是否为 NetBIOS 查询报文。若查询名称与设备名称 NETBIOS_W55MH32_NAME 匹配,则通过 getSIPR() 获取自身 IP,构造响应报文并通过 sendto() 发送给请求端。
步骤3:netbios_name_decoding 名称解码
文件:USER/netbios/netbios.c — NetBIOS 名称在传输时经过一级编码(每个字符被编码为两个字符)。netbios_name_decoding() 函数负责将编码后的名称解码还原,以便与设备自身的 NetBIOS 名称进行比对。
static int netbios_name_decoding(char *name_enc, char *name_dec, int name_dec_len)
{
char *pname;
char cname;
char cnbname;
int index = 0;
// Decode the name of the former NetBIOS
pname = name_enc;
for (;;)
{
/* Every two characters of the first level-encoded name
* turn into one character in the decoded name. */
cname = *pname;
if (cname == '\0')
break; // no more characters
if (cname == '.')
break; // scope ID follows
if (cname < 'A' || cname > 'Z')
{
// Not legal.
return -1;
}
cname -= 'A';
cnbname = cname << 4;
pname++;
cname = *pname;
if (cname == '\0' || cname == '.')
{
/* No more characters in the name - but we're in
* the middle of a pair. Not legal. */
return -1;
}
if (cname < 'A' || cname > 'Z')
{
// Not legal.
return -1;
}
cname -= 'A';
cnbname |= cname;
pname++;
// Do we have room to store the character?
if (index < NETBIOS_NAME_LEN)
{
// Yes - store the character.
name_dec[index++] = (cnbname != ' ' ? cnbname : '\0');
}
}
return 0;
}
解码原理:每两个编码字符还原为一个原始字符。第一个编码字符减去 'A' 后左移 4 位,第二个编码字符减去 'A' 后进行或运算,组合成一个字节即为原始字符。解码后的名称与 NETBIOS_W55MH32_NAME 比对,匹配则回复响应。
运行验证
烧录例程运行后,首先进行 PHY 链路检测,然后通过 DHCP 获取网络地址并打印,最后程序开始持续接收和响应 NetBIOS 请求。打开 WIZ UartTool V1.0,连接对应 COM 口(115200 / 8N1),预期串口输出如下:
W55MH32 NetBIOS 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
====================================================================================================
please try ping W55MH32
rem_ip_addr=192.168.1.20:137
netbios name query question
name is W55MH32
send response
请注意
测试实例需要 PC 端和 W55MH32 处于同一网段。在 PC 端打开 CMD,使用 ping W55MH32 即可通过名称访问设备,无需关心设备的 IP 地址。


常见问题
Q: NetBIOS 使用什么端口和协议
NetBIOS 名称服务使用 UDP 137 端口,数据报服务使用 UDP 138 端口,会话服务使用 TCP 139 端口。本例程中 W55MH32 主要使用 UDP 137 端口来监听和响应 NetBIOS 名称查询请求。
Q: 为什么 PC 端可以直接通过名称 ping 设备
当 PC 端 ping 一个 NetBIOS 名称时,会先查询本地缓存,若无记录则发出 NetBIOS 广播请求。W55MH32 收到请求后检查名称是否匹配,若匹配则回复自身 IP 地址。PC 端建立名称与 IP 的映射后,即可将名称替换为 IP 进行 ping 操作。
Q: NetBIOS 名称有什么限制
NetBIOS 名称长度为 16 个字符,在网络中作为节点的唯一标识。名称经过特殊编码(一级编码)后传输,每个字符被编码为两个字符。netbios_name_decoding() 函数负责将编码后的名称解码还原。
Q: 设备不在同一网段能否使用 NetBIOS
NetBIOS 名称查询基于 UDP 广播,广播报文通常只在同一网段内传播。因此 PC 端和 W55MH32 需要处于同一网段才能正常进行 NetBIOS 名称解析。若跨网段使用,需要通过网络中的 NBNS(NetBIOS Name Server)来转发查询请求。
Q: do_netbios() 函数的状态机是如何工作的
do_netbios() 基于 UDP socket 状态机运行。SOCK_CLOSED 状态下初始化 UDP socket 并绑定 NETBIOS_PORT 端口;SOCK_UDP 状态下接收数据,判断是否为 NetBIOS 查询报文,若名称匹配则构造并发送响应报文。整个过程在主循环中持续运行。
总结
- 协议定位:NetBIOS 是 IBM 开发的局域网名称解析协议,通过 UDP 137 端口工作,实现基于名称的通信
- 核心功能:为网络节点分配唯一 16 字符名称,支持动态注册与解析
- 工作流程:PC 查询缓存 → 广播请求 → 设备匹配名称并回复 IP → PC 建立映射 → ping 操作
- 实现方式:do_netbios() 构建 UDP 状态机,监听查询报文;netbios_name_decoding() 解析编码名称
- 应用价值:解决 DHCP 动态分配 IP 后设备难以访问的问题,可直接通过名称访问设备