最可靠方式是直接调用各平台原生API:Windows用WlanScan+通知机制,Linux用nl80211 netlink,macOS用CoreWLAN并配置entitlement;跨平台需封装统一接口,iOS则完全不可用。

Windows 上用 WlanEnumInterfaces 和 WlanScan 获取扫描结果
直接调用 Windows Native WiFi API 是最可靠的方式,绕过 PowerShell 或 netsh 的封装开销和权限限制。关键不是“能不能扫”,而是“扫完怎么等结果回来”——WlanScan 是异步触发,必须配合 WlanRegisterNotification 监听 wlan_notification_acm_scan_complete 事件,否则立刻调 WlanGetNetworkBssList 只会返回空或过期数据。
常见错误是忽略接口句柄生命周期:hClient 必须由 WlanOpenHandle 创建,且在所有操作后配对调用 WlanCloseHandle;若重复调用 WlanOpenHandle 而未关闭,后续 WlanEnumInterfaces 可能返回 ERROR_INVALID_PARAMETER。
- 调用
WlanScan后需用MsgWaitForMultipleObjects等待通知,不能 Sleep(1000) 硬等 -
WlanGetAvailableNetworkList返回的是配置过的网络(即连过的历史 SSID),不是实时扫描结果;要用WlanGetNetworkBssList - BSS 列表里的
dot11Ssid.ucSSID是字节数组,需按dot11Ssid.uSSIDLength截取,不是 null-terminated 字符串
Linux 下通过 nl80211 socket 发送 SCAN_REQ 并解析 NLMSG
ioctl + SIOCGIWSCAN 在现代内核已弃用,必须走 netlink。核心是构造 NL80211_CMD_TRIGGER_SCAN 消息,发给 NETLINK_GENERIC socket,并监听 NL80211_CMD_SCAN_DONE 回应。难点不在发送,而在解析嵌套的 nlattr:SSID、信号强度(NL80211_BSS_SIGNAL_MBM)、频率都藏在 NL80211_ATTR_BSS 的子属性里,需用 nla_parse_nested 逐层剥开。
权限上,普通用户默认无权访问 nl80211,需将进程加入 net_admin capability,或改用 dbus 代理(如 org.freedesktop.NetworkManager 的 GetAccessPoints 方法),但后者依赖 NM 运行且不保证实时性。
- 扫描前必须先用
NL80211_CMD_GET_INTERFACE查出目标无线接口的ifindex,不能硬编码 wlan0 - 信号值单位是 dBm × 100(如 -6523 表示 -65.23 dBm),直接除以 100 再转整数易丢精度,建议保留原值做比较
- 部分驱动(如 rtl88x2bu)在并发扫描时可能丢包,需检查
NL80211_CMD_NEW_SCAN_RESULTS是否重复到达
macOS 需调用 CoreWLAN 框架,且必须处理 sandbox 权限
CWInterface 的 scanForNetworksWithName:error: 方法看似简单,但实际运行时大概率抛 kCWNoIdleInterfaceErr 或静默失败——根本原因是 macOS 12+ 默认启用 App Sandbox,而 com.apple.wifi.scan entitlement 不再自动包含,必须手动在 Xcode 的 Signing & Capabilities 中勾选 “WiFi Information” 并提交 Apple 审核(仅限 Mac App Store 分发)。非 MAS 应用只能用私有 API(如 apple80211Scan),但会导致 Gatekeeper 拒绝启动。
另一个坑是线程模型:scanForNetworksWithName: 是同步阻塞调用,但底层会切到内部队列执行,若在主线程调用且超时(默认 10 秒),UI 会卡死;务必用 dispatch_async 包一层。
- 扫描结果中的
CWNetwork对象,signalStrength返回的是 RSSI 值(-100 ~ 0),但部分 802.11ax 设备可能返回 0,需结合channelWidth和securityMode交叉验证 - 若系统开启了“私人地址”(Private Wi-Fi Address),扫描到的 BSSID 会是随机 MAC,无法用于设备指纹
- 后台运行时扫描会被系统暂停,
UIApplication生命周期回调不可靠,macOS 上无等效机制
跨平台抽象时别碰 std::system("netsh wlan show networks")
这种写法在 Windows 上看似省事,但输出格式随系统语言变化(中文 Win10 返回“可见网络”,英文则为 “Visible Networks”),且 netsh 扫描缓存延迟高(默认 90 秒才刷新),无法满足实时性要求。更严重的是,它会弹出黑窗、触发 UAC 提权提示(若进程无管理员权限),并让杀软标记为可疑行为。
真正可行的跨平台路径只有两条:一是用 C++ 封装各平台原生 API,暴露统一接口(如 WiFiScanner::startScan());二是引入轻量级绑定库,如 libiw(Linux)、CoreWLANCpp(macOS)、wifiutil(Windows),但要注意后者大多停止维护,头文件里常含已废弃的 WlanQueryInterface 调用。
- 不要尝试解析
/proc/net/wireless—— 它只显示当前连接的 AP 信号,不是扫描列表 - Qt 的
QNetworkConfigurationManager不提供扫描能力,仅管理已知配置 - 即使用了原生 API,iOS 完全禁止第三方应用扫描 WiFi,此功能在 iOS 上注定不可用
真实项目里,最难的往往不是调通某个函数,而是处理驱动兼容性断层:比如 Intel AX200 在 Linux 上需 iwlwifi 固件 v48+ 才支持主动扫描,而旧版固件只返回被动扫描的 beacon;这类问题不会报错,只会让你拿到空列表,然后花半天怀疑代码逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











