起因:一间宿舍,值不值得买个路由器?

我住公司宿舍,自己拉了宽带。房间不大,一根网线进门,接到电脑上就完事了。至于手机和平板,本来想着买个路由器,但转念一想:

  • 房间就这么大,电脑的无线网卡覆盖整个屋子绰绰有余;
  • 公司宿舍免电费,电脑本来就常年开着;
  • 电脑上跑着 sing-box,代理分流和广告过滤都配好了。如果手机、平板直接连电脑开的热点,这些规则原封不动地就继承过去了,连软路由都省得折腾。

省钱、省事、还省了一台设备。于是 Windows 设置里点开「移动热点」,手机平板连上,完美。

直到我发现,手机隔三差五就会莫名其妙切到流量。

症状:手机以为自己连着,其实早就断了

具体表现是这样的:

  • 手机 Wi-Fi 图标一直在,信号满格;
  • 但网页打不开,过一会儿系统自己跳到了移动数据;
  • 电脑这边热点还开着,平板也时不时出同样的问题;
  • 手动关掉 Wi-Fi 再打开,立刻就好了。

一开始我以为是手机的「智能切换」太敏感,于是插上 USB,用 adb 看看手机到底是怎么想的。

第一站:手机为什么认为 Wi-Fi 不可用

dumpsys connectivity 里 Wi-Fi 被标成了 EXITING、isUsable:false,默认网络已经换成了移动数据。往前翻 dumpsys wifi 的记录,时间线非常清楚:

20:39:09 DATA_STALL_BAD_TX  txSuccessDelta=0 txRetriesDelta=64 txBadDelta=5 rxSuccessDelta=0
20:39:14 wlan_assist 申请建立移动数据
20:39:17 FIRMWARE_ALERT ...
20:39:51 IpReachabilityMonitor: 192.168.5.1 NUD_FAILED   ← 连网关都不通了

两秒内发了 64 次重传,一个都没成功,也一个包都没收到。可当时的信号强度是 -41 dBm,非常好。信号好却发不出去,不像是干扰,更像是对面根本不理它了。

我一开始还怀疑是路由器兼容性,直到想起来:这个“路由器”就是我自己的电脑。

第二站:交叉对比电脑日志

电脑这边打开事件查看器里的 Microsoft-Windows-WLAN-AutoConfig/Operational,两边时钟对齐(只差 0.5 秒),结果一目了然:

时间电脑(热点)手机
20:38:4011004 Wireless security stopped,手机的会话被结束毫无察觉,依然“已连接”
20:39:09—发包全部失败
20:39:14—切到移动数据
20:40:35手机重新连上恢复

是电脑先把手机踢了,而且没通知手机。 手机那边还挂着一个死连接,要等自己的检测机制超时才会切走。

第三站:挨个排除,全都没用

接下来就是经典的“网上能搜到的方法都试一遍”环节:

  1. 电源管理:网卡「允许计算机关闭此设备」本来就是关的,电源计划里无线适配器也是最高性能。❌
  2. 更新网卡驱动:Intel AX201 从 24.10 升到 24.70(2026 年 7 月的版本)。❌ 照断不误。
  3. 关闭数据包合并(Packet Coalescing)。❌
  4. 怀疑后台扫描:电脑的 WLAN 在当热点的同时还会扫描周围网络,扫描时要切离信道。于是把所有保存的 Wi-Fi 改成手动连接,再用 netsh wlan set autoconfig enabled=no interface="WLAN" 关掉自动配置。❌ 还是断。

第四站:抓驱动日志,找到真正的“凶手”

既然表面的设置都没用,那就看看底层到底发生了什么。用 netsh trace 抓 WLAN 相关的 ETW 日志,等它断一次:

netsh trace start provider=Microsoft-Windows-WLAN-AutoConfig provider=Microsoft-Windows-NWiFi `
  provider=Microsoft-Windows-WLAN-Driver provider=Microsoft-Windows-WiFiHotspotService `
  capture=no report=disabled maxsize=1024 filemode=circular tracefile=wlan.etl

断开那一瞬间的记录:

22:59:24.231 Received Security Packet: EXTAP_PORT_DOWN
22:59:24.231 FSM Current state AP_Started, event Upcall_Port_Down
22:59:24.231 Port down for peer 0x2E080E88xxxx          ← 手机的 MAC
22:59:24.231 SecMgr Transition SUCCESS --> DEACTIVATE --> INACTIVE
22:59:24.231 RequestAdapterSync Error: 0xC0000001
22:59:24.231 Wireless security stopped.

Upcall_Port_Down 是网卡驱动主动上报给 Windows 的:“这个客户端下线了”。之前没有任何 Windows 发起的踢人、密钥更新失败或者服务报错。热点服务、ICS、电源管理全都是被动接到通知的。

至于驱动为什么这么判断,具体原因写在 Intel 的私有日志里,外人解不出来。

后来一搜,发现这不是我一个人的问题。Intel 社区从 2022 年起就有人报告 AX201 + Windows 移动热点,手机空闲熄屏后被踢,症状一模一样,普遍的说法是驱动 22.160 之后就出问题了,大家的办法基本是回退老驱动。我装的已经是 24.70,四年过去了,依然没修。

解决:一个“土办法”

回退老驱动我不太想。太老的驱动在新系统上不一定装得上,Windows 更新也可能又给你升回去。

换个思路:从现象看,设备一空闲、手机熄屏省电,就会被踢。驱动大概是一段时间收不到对方的帧,就按“不活跃”处理了。

那就别让它空闲。电脑定时往热点下的每台设备发个包,让链路一直有东西在走。

最朴素的版本就是每隔几秒 ping 一次。挂上之后,真的不断了。

做成了一个小工具

光一个 ping 循环不太好用,就顺手包装了一下:

  • 自动发现热点下的所有设备:读热点网卡的 ARP 表,按 MAC 记住设备,IP 变了也能跟上;
  • 两种模式:自动保活全部设备,或者只保活勾选的设备;
  • 图形界面:启动、停止、注册开机服务、移除开机服务;
  • 后台运行和界面分离:关掉界面,保活照样跑。注册开机服务后,以 SYSTEM 身份开机自启,不用登录;
  • 日志:记录设备连续失联、恢复,以及热点的断开事件,方便判断是真被踢了还是暂时没回应。

核心逻辑其实就这么几行:

$if = (Get-NetIPAddress -IPAddress $gateway).InterfaceIndex
$clients = Get-NetNeighbor -InterfaceIndex $if -AddressFamily IPv4 |
    ? { $_.State -ne 'Permanent' -and $_.LinkLayerAddress -notmatch '^(00-){5}00$|^(FF-){5}FF$' }
foreach ($c in $clients) {
    (New-Object System.Net.NetworkInformation.Ping).SendPingAsync($c.IPAddress, 800) | Out-Null
}

实验性功能:硬件 ACK 模式

ping 有个小代价:手机收到 ping 要唤醒 CPU 来回复。间隔 10 秒的话,一天就是八千多次短暂唤醒。

但仔细想想,真正让网卡觉得“设备还活着”的,很可能不是 ping 的回包,而是更底层的东西:电脑发出任何一个无线帧,手机的 Wi-Fi 芯片都会在硬件层自动回一个 802.11 ACK。

所以我加了个「硬件 ACK 模式」:不 ping,改为用 WinPcap 往设备的 MAC 地址直接发一个 60 字节的空以太网帧,协议类型用 0x88B5(IEEE 保留给本地实验用的)。

  • 手机芯片收到后会回 ACK,这一步不需要 CPU;
  • 这种没人认识的协议类型,系统一般会直接丢掉,理论上不会唤醒手机。

实测开着这个模式,手机和平板也没有再被踢过。不过要说明两点:

  1. “不唤醒手机 CPU”目前只是推理,我还没有实测耗电;
  2. 它需要装 WinPcap 或 Npcap。这个模式发出去收不到回复,没法判断设备在不在线,只能通过热点的断开事件来确认有没有被踢。

脚本地址:hotspot-keepalive

使用前请注意

  • 适用场景:Windows 移动热点,网卡是 Intel(至少 AX201 确认有这个问题),客户端空闲后被踢、手机显示“已连接但无法上网”。
  • 网关地址:我的热点网段改成了 192.168.5.x。Windows 默认是 192.168.137.1,用之前在界面里改成你自己的。
  • 耗电:ping 模式会让客户端少睡一点,间隔可以按需调大,找到刚好不断线的值。
  • 治标不治本:这是在绕过驱动的行为,不是修复驱动。哪天 Intel 修好了,这个工具就可以删了。

顺便说几个排查中的发现

  • 手机自己也在 ping 网关:抓包发现 ColorOS 大约每 7 秒会 ping 一次热点网关,应该是系统在检测链路质量。但这点流量显然不够让网卡满意。
  • PowerShell 5.1 的坑:函数返回单个 CIM 对象时,.Count 是空的。我的界面一度把“有 1 个在运行”判断成“没在运行”,还会重复启动。老老实实用 @(...).Length。
  • 中文脚本要带 BOM:PowerShell 5.1 读取不带 BOM 的 UTF-8 脚本会按系统代码页解析,中文全乱。

参考