windows用getadaptersaddresses(需两次调用处理缓冲区、检查operstatus和physicaladdresslength)、linux用getifaddrs(需过滤iff_up/iff_running且排除lo),无跨平台标准api,必须条件编译实现。

Windows 和 Linux 获取网络适配器列表的 API 完全不同,没有跨平台标准接口;直接写两套逻辑 + 条件编译是最稳的做法,别试图用第三方库“一劳永逸”——除非你已明确引入 libpcap 或 boost::asio 且接受其依赖和行为差异。
Windows 下用 GetAdaptersAddresses 拿完整信息
这是微软推荐的现代方式(替代已废弃的 GetAdaptersInfo),能拿到 IPv4/IPv6 地址、MAC、状态、描述等。关键点不是调用,而是内存管理和错误重试:
-
GetAdaptersAddresses第一次调用传NULL会返回所需缓冲区大小,必须按此分配再重试,不能硬写大数组 - 返回值是
ERROR_BUFFER_OVERFLOW(不是FALSE)才说明要重分配,其他非零值才是真错误 - 结果链表里的
FirstUnicastAddress是 IPv4/IPv6 地址链表,需遍历并检查Address.lpSockaddr->sa_family - MAC 地址在
PhysicalAddress字段,长度由PhysicalAddressLength决定,不是固定 6 字节(某些虚拟网卡可能更短)
示例片段(简化版):
ULONG size = 0;
GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &size);
auto buf = std::make_unique<byte>(size);
auto ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr,
reinterpret_cast<ip_adapter_addresses>(buf.get()), &size);
</ip_adapter_addresses></byte>
Linux 下用 getifaddrs 更轻量但需手动过滤
getifaddrs 是 POSIX 标准函数,无需额外链接,但返回的是所有接口地址(含 lo、tun、docker0 等),且不直接提供 MAC 或状态标志,得自己判断:
在 Windows 上执行终端命令,或对比 Windows 与 Linux 的 Shell 语法时使用。建议先检测当前 Shell 是 PowerShell 还是 cmd.exe,再给出相应命令……
- 遍历链表时,用
ifa->ifa_addr->sa_family过滤AF_PACKET(对应 MAC)或AF_INET/AF_INET6(IP 地址) - MAC 地址从
struct sockaddr_ll*的sll_addr取,注意sll_halen才是真实长度 - 接口是否 up 要查
ifa->ifa_flags & IFF_UP,但IFF_RUNNING才表示物理连通(比如网线拔了,UP 但不 RUNNING) - 别忘了最后调用
freeifaddrs,否则内存泄漏
常见误判:把 lo 当成可用网卡,或把 vethxxx 当成主机网卡——建议按 ifa->ifa_flags & (IFF_UP | IFF_RUNNING) 且 strcmp(ifa->ifa_name, "lo") != 0 做基础筛选。
跨平台封装要注意的三个硬伤
即使你写了 struct AdapterInfo 统一字段,以下三点仍无法掩盖底层差异:
- Windows 的 “描述”(
AdapterName/Description)在 Linux 下无直接对应,/sys/class/net/{name}/device/driver软链可能为空,只能 fallback 到if_name - IPv6 地址前缀长度在 Windows 里藏在
PrefixLength字段,在 Linux 需从sockaddr_in6后面的struct ifa_msghdr或/proc/net/if_inet6解析——getifaddrs不提供 - 权限问题:Linux 下读取某些虚拟网卡 MAC 可能因权限不足返回空(
EPERM),而 Windows 默认就能读;不要假设所有接口都能拿到 MAC
别碰 ioctl(SIOCGIFCONF) 和注册表枚举
这两个是旧式方案,现在纯属自找麻烦:
-
SIOCGIFCONF返回的是struct ifreq数组,不带 IPv6、无状态标记、MAC 获取要额外发SIOCGIFHWADDR,代码量翻倍且易出错 - Windows 注册表路径
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}下枚举子键再查NetCfgInstanceId,不仅慢,还会漏掉 WSL2 虚拟网卡、NDIS Light-Weight Filter 等新类型 - 两者都不支持热插拔通知,而
GetAdaptersAddresses和getifaddrs至少是快照一致的
真正难的从来不是“怎么拿到列表”,而是后续怎么定义“一个可用的、有公网能力的、用户想用的”适配器——那得结合路由表、DNS、连接测试,不在这个函数职责范围内。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










