为何Windows 11中getnameinfo无法返回本地设备的主机名?
问题排查与解决方案
代码中的关键问题
错误处理逻辑错误
getnameinfo的返回值dwRetval本身就是错误码,不需要调用WSAGetLastError()。解析失败时,应直接用gai_strerror(dwRetval)获取错误描述,WSAGetLastError()在此场景下无效。NI_NAMEREQD标志误用
该标志强制要求必须解析出DNS反向记录,否则直接返回失败。但路由器和手机APP显示的设备名称大多是NetBIOS名称或MDNS(多播DNS)名称,并非DNS反向解析记录,使用此标志会导致多数正常场景被判定为失败。过时的地址转换函数
inet_addr已被废弃,建议使用inet_pton(AF_INET, argv[1], &saGNI.sin_addr),它能更可靠地校验IPv4地址格式,避免无效地址引发的问题。不必要的端口设置
若仅需获取主机名,无需指定具体端口(如代码中的27015),可将saGNI.sin_port设为htons(0),因为服务信息并非目标需求。
修正后的测试代码
#include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> #pragma comment(lib, "Ws2_32.lib") int __cdecl main(int argc, char** argv) { WSADATA wsaData = { 0 }; int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } if (argc != 2) { printf("usage: %s IPv4 address\n", argv[0]); printf(" %s 192.168.1.100\n", argv[0]); WSACleanup(); return 1; } struct sockaddr_in saGNI = {0}; char hostname[NI_MAXHOST]; char servInfo[NI_MAXSERV]; saGNI.sin_family = AF_INET; if (inet_pton(AF_INET, argv[1], &saGNI.sin_addr) != 1) { printf("Invalid IPv4 address\n"); WSACleanup(); return 1; } saGNI.sin_port = htons(0); // 无需服务信息,设为0 DWORD dwRetval = getnameinfo((struct sockaddr*)&saGNI, sizeof(struct sockaddr_in), // 使用sizeof(sockaddr_in)更准确 hostname, NI_MAXHOST, servInfo, NI_MAXSERV, 0); // 移除NI_NAMEREQD,允许无解析结果时返回IP if (dwRetval != 0) { printf("getnameinfo failed: %s\n", gai_strerror(dwRetval)); } else { printf("Resolved name: %s\n", hostname); } WSACleanup(); return 0; }
更可靠的设备名称获取方法
若修正后的代码仍无法获取路由器/手机APP显示的名称,说明目标设备未配置DNS反向解析,可尝试以下方法:
- NetBIOS名称查询:Windows系统可通过NetBIOS协议获取局域网设备名称,可调用
NetBIOS()核心API,或参考nbtstat命令的实现逻辑(向目标IP的137端口发送NetBIOS查询包)。 - MDNS查询:多数智能设备会通过MDNS广播自身名称(格式为
设备名.local),Windows 11支持DNS-SD API,可使用DnsServiceBrowse等函数查询局域网内的MDNS设备。 - ARP缓存结合NetBIOS:先通过ARP缓存获取局域网内的IP-MAC映射,再对每个IP执行NetBIOS名称查询,此逻辑与路由器设备列表的实现方式更接近。
内容的提问来源于stack exchange,提问作者Sid
相关产品推荐
相关产品推荐

