Skip to content

Windwos下启用WOL功能

约 5828 字大约 19 分钟

WindowsWOL

2025-05-10

前言

Wake-on-LAN(WOL, 网络唤醒)是一种以太网或令牌环计算机标准,允许通过网络消息开启或唤醒睡眠模式的计算机。该消息通常由连接到同一局域网 (LAN) 的设备上运行的程序发送到目标计算机。也可以通过使用子网定向广播或 WoL 网关服务从另一个网络发起消息。它基于 AMD 的 Magic Packet 技术 ,该技术由 AMD 和惠普共同开发,并于 1995 年被提议作为标准。

简而言之,就是能在局域网下通过发送魔术包的方式唤醒计算机。这有什么好处呢?

试想一个场景,你的家里有一台配置比较高的电脑或服务器,安装一些开发环境,启动了一些服务:Nginx、Redis、Mysql、Kafka 等等。由于配置高又运行这么些服务,功耗也同样高,所以我们必然不会全天候运行,所以想过弄一个低功耗的小主机/小型服务器,7x24 小时不间断运行。 我考虑过迷你主机、Nas主机,但它们被设计的使用场景都不符合我的需求。看过不少品牌的迷你主机,它的性能通常比Nas高,但又不如我们的标准主机,而且它的扩展性也不如标准主机,如果运行我们开发所需的服务,它的功耗一样不会低,那么小的机身还要给它散热,有点太为难它的,那要它做甚?
至于Nas,它的设计目标在于数据存储和共享,通常是 7x24 小时不间断运行,所以要在功耗和性能上做平衡。
说来说去,又回到了问题的起点。那就换一种解决思路,能不能在需要的时候开启,不用的时候关闭呢?当然可以,这就需要用到 WOL 技术了。

1. 设置唤醒

要想开启 Windows 的 WOL 功能有两个部分需要设置:BIOS设置、网卡设置。

1.1 BIOS设置

不同主板进入 BIOS 的方式不同,都可以谷歌得到,这里以我的微星主板为例。
电脑开启时按DEL键进入,在SETTING -> 高级 -> 唤醒事件设置,允许网络唤醒即可。同时还有一项PCIE设备唤醒,字面意思很容易理解,网卡就是一种PCIE设备,所以允许网络唤醒其实也就是允许PCIE设备唤醒,只不过微星把这两个概念拆开了。可能在有的主板上就只有一项PCIE设备唤醒,打开它也是一样的。但PCIE设备并不仅仅只有网卡,其他任何一个开发板只要是有 PCIE 接口可以插在主板上,那么它也可以成为唤醒设备,这其实就是留了一个开放接口的意思,你可以做各种 DIY 实现。这样,我们就可以实现电脑在睡眠/休眠中被唤醒了。

另外,在电源管理设置中有一项ERP/ErP Ready,它默认是打开的,如果想要电脑在关机下也能被唤醒,那就需要关闭它。ErP的意思是与能源有关的产品,它与欧盟对各种电子产品(包括个人电脑)通过的环境法规有关。启用 BIOS 里的 ErP 模式或使用 ErP Ready 设置,将使你的个人电脑在关闭时,可以关闭流向所有组件的电源,将总的电力消耗减少到1瓦或更少。
说人话就是,禁用 ErP 在关机下几乎不耗电池,此时也不允许你用鼠标或键盘唤醒电脑,不能对传入的局域网信号作出反应,也就是说远程网络唤醒功能也因此失效了,那我们自然要禁用它。可能你会担心禁用后会好很多电吗?实际上并不会,禁用 ErP 后也就比启用它在关机下的功能多 1~2W,但这根本无所谓。按照民电 0.68元/度,2W 的设备 7x24 小时不间断运行,一年下来需要 11.91元的电费,相对于你在这一年里的开销来说,这不过于九牛一毛。

1.2 网卡设置

开机进入系统,按下win+R输入ncpa.cpl会打开网络连接,选择你当前使用的那个网卡(插的是网线就选以太网,连的 wifi 就选WLAN),找到属性 -> 配置 -> 电源管理,勾选允许此设备唤醒计算机只允许幻数据包唤醒计算机(可选,但建议开启),并取消勾选允许关闭计算机关闭此设备以节约电源

2. 电源状态

在 ACPI(高级配置与电源接口)规范中,定义了从 S0 到 S5 的一系列电源状态,用于描述操作系统如何控制计算机的电源管理。

状态名称描述
S0工作状态(Working)系统完全运行,所有硬件设备正常供电
S1待机状态(Standby)CPU 停止执行指令,但保持供电,内存保留内容,唤醒非常快
S2深度待机状态(Deeper Sleep)类似 S1,但 CPU 和缓存完全断电;更省电,唤醒稍慢。很少使用
S3睡眠状态(Sleep/Suspend to RAM)内存保持供电,CPU 和外设断电;唤醒速度较快,常见的“睡眠”模式
S4休眠状态(Hibernate/Suspend to Disk)将内存内容保存到硬盘,然后全部断电;唤醒时从磁盘恢复,速度较慢,但更省电
S5关机状态(Soft Off)系统完全关闭,几乎无功耗,但仍可通过特定事件(如唤醒信号)启动

可能之前我们会把睡眠(S3)和休眠(S4)弄混淆,事实上它们的定义标准是不同的。由于 RAM 的读写速度非常高,当我们从睡眠模式下唤醒到进入系统页面,通常只需要 2~3 秒;但硬盘的读写速度远不如 RAM ,即便是一个 SSD 硬盘,从休眠模式下唤醒到进入系统页面,可能也需要 7~8 秒。
在 Windows11 系统中,我们发现只能看到睡眠,其实默认下休眠是被隐藏了,因为 Windows11 系统的关机休眠就比较类似了,一样是把工作内容保存在硬盘,使得开机后可以迅速恢复。

按下win+R输入control打开控制面板,选择电源选项。在左侧的选择电源按钮的功能中可以看到休眠是没有被勾选的,我们可以勾选上,这样在按下Win键并点击关机按钮时就可以看到休眠的选项了。
同时,我们注意的电源选项中还有一项启用快速启动(推荐),它是默认被勾选的,正是由于启用了它,才得以让关机的效果和休眠类似,Windows 刻意隐藏了休眠开关,想用快速启动下的关机来达到休眠效果,也避免了休眠睡眠的含义比较接近,导致用户感觉到歧义。

此时,电脑就设置好了 WOl 唤醒了。可以通过Fing应用(支持Android和IOS)其中的LAN唤醒来验证,IOS 系统亦可下载Awake应用(免费无广,且可集成在快捷指令中)验证。如果有 OpenWrt 的软路由系统,可以安装luci-i18n-wol-zh-cn,将网络唤醒集成在 OpenWrt 中,便于操作。

至此,就已经设置好了主机网络唤醒。

3.设置休眠

上面说的都是把主机从睡眠/休眠/关机状态下唤醒,那么我们还想要在主机醒着的状态下进入到睡眠/休眠/关机状态如何做呢?这里当然不是要说在电脑上通过点击来操作,这样没有意思,我们需要通过指令的方式让主机进入到睡眠/休眠/关机状态。

通过命令行(cmd)窗口执行shutdown --help可以看到指令支持的操作如下:

用法: shutdown [/i | /l | /s | /sg | /r | /g | /a | /p | /h | /e | /o] [/hybrid] [/soft] [/fw] [/f]
    [/m \\computer][/t xxx][/d [p|u:]xx:yy [/c "comment"]]

    没有参数   显示帮助。这与键入 /? 是一样的。
    /?         显示帮助。这与不键入任何选项是一样的。
    /i         显示图形用户界面(GUI)
               这必须是第一个选项。
    /l         注销。这不能与 /m 或 /d 选项一起使用。
    /s         关闭计算机。
    /sg        关闭计算机。在下一次启动时,如果启用了
               自动重启登录,则将自动登录并锁定上次交互用户。
               登录后,重启任何已注册的应用程序。
    /r         完全关闭并重启计算机。
    /g         完全关闭并重启计算机。重新启动系统后,
               如果启用了自动重启登录,则将自动登录并
               锁定上次交互用户。
               登录后,重启任何已注册的应用程序。
    /a         中止系统关闭。
               这只能在超时期间使用。
               与 /fw 结合使用,以清除任何未完成的至固件的引导。
    /p         关闭本地计算机,没有超时或警告。
               可以与 /d 和 /f 选项一起使用。
    /h         休眠本地计算机。
               可以与 /f 选项一起使用。
    /hybrid    执行计算机关闭并进行准备以快速启动。
               必须与 /s 选项一起使用。
    /fw        与关闭选项结合使用,使下次启动转到
               固件用户界面。
    /e         记录计算机意外关闭的原因。
    /o         转到高级启动选项菜单并重新启动计算机。
               必须与 /r 选项一起使用。
    /m \\computer 指定目标计算机。
    /t xxx     将关闭前的超时时间设置为 xxx 秒。
               有效范围是 0-315360000 (10),默认值为 30
               如果超时期限大于 0,则 /f 参数为
               /f 参数。
    /c "comment" 注释重启或关闭的原因。
               最多允许 512 个字符。
    /f         强制关闭正在运行的应用程序而不事先警告用户。
               当大于 0 的值为
 时,隐含 /f 参数               则默示为 /f 参数。
    /d [p|u:]xx:yy  提供重新启动或关闭的原因。
               p 指示重启或关闭是计划内的。
               u 指示原因是用户定义的。
               如果未指定 p 和 u,则
重新启动或关闭               是计划外的。
               xx 是主要原因编号(小于 256 的正整数)
               yy 是次要原因编号(小于 65536 的正整数)

此计算机上的原因:
(E = 预期 U = 意外 P = 计划内,C = 自定义)

这里关注几个我们需要的:

指令效果
shutown /s关机
shutown /h休眠
shutown /r重启

但是,我们没有看到睡眠的指令。经过一番搜索,找到两条指令可以实现睡眠或休眠的效果:rundll32.exe powrprof.dll,SetSuspendState 0,1,0rundll32.exe powrprof.dll,SetSuspendState Sleep。这两种指令经过验证效果是一样的。
为什么说是实现睡眠或休眠的效果呢?即睡眠休眠只能二选一,那什么时候是睡眠,什么时候是休眠呢?

默认情况下,休眠功能是启用的,那么上面的指令效果就是休眠,则它等价于shutown /h。可以通过powercfg -h off(powershell 管理员权限)来关闭休眠功能,此时shutdown /h指令会失效,同时在电源选项中亦不存在休眠启用快速启动(推荐)的设置,而指令的效果也就变成睡眠了。所以,需要斟酌一下,到底要不要关闭休眠功能。如果关闭休眠后想再次打开,可执行powercfg -h on
如何分辨是从睡眠中醒来,还是从休眠中醒来?查看主机唤醒时,显示器有没有显示主板BIOS的图案,如果有就说明是从休眠中醒来,否则不是。

4. 进阶操作

如何便捷地通过 iPhone 在局域网下唤醒/休眠主机?这里我总结了一套实践经验。
因为 IOS 的快捷指令中支持 SSH 连接并发送指令,那么完全可以通过 SSH 连接到 OpenWrt/Windows 来实现唤醒/休眠主机,但是考虑到操作统一性和便捷性,我实际的做法是:

4.1 Windows 部分

触发脚本

然而在实操踩坑中发现,对于shutdown /s(关机)和shutdown /r(重启)的指令响应逻辑没问题,但是睡眠休眠就有问题了,直接执行相关命令会导致,主机刚进入睡眠休眠状态又立即被唤醒了。
因此我在 Windows 用户目录下放了两个bat脚本,定义了临时触发器来执行操作,供 OpenWrt 通过 SSH 调用,具体如下。

trigger_sleep.bat
@echo off
setlocal

:: 获取当前时间 + 1 分钟
for /f "tokens=1,2 delims=:" %%a in ("%time%") do (
    set /a hh=1%%a %% 100
    set /a mm=1%%b + 1
)
if %mm% GEQ 60 (
    set /a mm=0
    set /a hh+=1
)
if %hh% GEQ 24 set /a hh=0

:: 格式化为 HH:MM(确保补零)
set hh=0%hh%
set mm=0%mm%
set hh=%hh:~-2%
set mm=%mm:~-2%

:: 创建一次性计划任务,执行“睡眠”
schtasks /create /tn "TriggerSleep" /tr "rundll32.exe powrprof.dll,SetSuspendState 0,1,0" /sc once /st %hh%:%mm% /f >nul
schtasks /run /tn "TriggerSleep" >nul

echo Will sleep at %hh%:%mm%
endlocal

这两个脚本,trigger_sleep.bat是用来触发睡眠指令,trigger_hibernate.bat是用来触发休眠指令。

开启SSH

如果想通过 SSH 来管理 Windows 系统,就需要安装OpenSSH服务,Windows11 默认已经安装了 SSH 的客户端,此时我们安装服务端。
有两种方式安装:

方式一:
进入设置->系统->可选功能->添加可选功能,搜索OpenSSH 服务器安装

方式二:

# 安装
Add-WindowsFeature -Name OpenSSH-Client, OpenSSH-Server
# 安装完成后就自动运行了
# 查看
Get-Service -Name sshd
# 连接测试
ssh localhost

设置为开机自启:按WIN键搜索计算机管理,然后打开服务找到OpenSSH SSH Server,修改属性将启动类型调整为自动(延迟启动)

SSH密钥登录

可以借助Git bash生成密钥,执行ssh-keygen -t rsa -f ~/.ssh/id_rsa(生成RSA密钥)或ssh-keygen -t ed25519 -f ~/.ssh/id_rsa(生成ed25519密钥),前者广泛兼容,后者安全性更高,随便选择一种即可。
然后把生成的密钥对中的公钥内容复制到 Windwos 用户目录下的.ssh\authorized_keys文件中,如果文件不存在可自行创建,注意文件没有后缀。示例:

authorized_keys
  ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCC9P8z96xo1EcfejdrvEt/6Tc2qhX6FhLevcqjhNvQYPYmQaMwZElsi1QAABAQCC9P8z96xo1EcfejdrvdrvEt/6Tc2q root@ImmortalWrt

接着就是 Windows 的特有操作,更改authorized_keys文件权限,否则无法通过密钥登录。以下命令二选一即可:

  # 远程通过 ACL 更改权限
  ssh --% mayee@192.168.0.88 icacls.exe "C:\Users\mayee\.ssh\authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
  # 服务端修改权限
  icacls.exe "C:\Users\mayee\.ssh\authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

在 Windows OpenSSH 中,默认的授权密钥存放位置为ProgramData\ssh\administrators_authorized_keys,此位置对应为管理用户权限。因此需要修改默认授权文件位置。通过文本编辑器(如 VSCode)打开ProgramData\ssh\sshd_config,修改以下条目(需要管理员权限,也可以把文件复制出去,改完后再覆盖回来):

sshd_config
  #允许公钥授权访问,确保条目不被注释
  PubkeyAuthentication yes

  #授权文件存放位置,确保条目不被注释
  AuthorizedKeysFile .ssh/authorized_keys

  #可选,关闭密码登录,提高安全性
  PasswordAuthentication no

  #注释掉默认授权文件位置,确保以下条目被注释
  #Match Group administrators
  #       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

  # 注意,OpenSSH 8.8 及以上版本中,默认禁用了使用 SHA-1 哈希算法的 RSA 签名,如果你用的 RSA 密钥就需要加这两行,ed25519 密钥则不需要加
  HostKeyAlgorithms +ssh-rsa
  PubkeyAcceptedAlgorithms +ssh-rsa

重启 OpenSSH 服务:在 PowerShell 中执行Restart-Service sshd(管理员权限)。
相关指令:

  Get-Service sshd  # 查看状态
  Stop-Service sshd # 关闭服务
  Restart-Service sshd # 重启服务

4.2 OpenWrt 部分

前文提到可以安装luci-i18n-wol-zh-cn,它是一个可视化的操作页面,功能实现依赖的是etherwake,也就是说只安装etherwake用命令行的方式操作就可以了,那样连 OpenWrt 后台页面都不用进。

可以在 OpenWrt 后台页面软件包中安装etherwake,或者执行命令opkg install etherwake

在 OpenWrt 中,LuCI(Lua Configuration Interface) 是其默认的 Web 界面管理系统,LuCI 本质是 一套用 Lua 编写的 Web 应用逻辑,是一种让 Web 服务器运行外部程序(通常是脚本)的标准接口协议,用来处理动态网页请求。但它并不自带服务器功能,它必须运行在一个 Web 服务器之上,而 uHTTPd 是 OpenWrt 默认提供的轻量级 Web 服务器。

文件说明
/etc/config/luciLuCI 的主配置文件,如语言、主题、界面行为等设置
/etc/config/uhttpduHTTPd 的配置文件,控制 Web 服务监听地址、端口、SSL 等
/etc/config/rpcd控制 LuCI 使用的 UBUS RPC 权限(例如哪些用户可以远程读写配置)

cat /etc/config/uhttpd查看配置文件:

uhttpd
config uhttpd 'main' 
        list listen_http '0.0.0.0:80'
        list listen_http '[::]:80'
        # 因为我们主要是内网访问,所以注释掉 443 端口的监听,后面留作他用
        #list listen_https '0.0.0.0:443'
        #list listen_https '[::]:443'
        option redirect_https '0' # http 不要自动重定向到 https
        option home '/www' # Web 根目录,访问静态页面文件的起点
        option rfc1918_filter '1' # 拒绝非私有网段的 IP 访问(例如来自公网 IP),防止 uhttpd 被意外暴露到公网
        option max_requests '50' # 每个进程最多处理 50 个请求
        option max_connections '100' # 最多同时允许 100 个连接
        option cert '/etc/uhttpd.crt'
        option key '/etc/uhttpd.key'
        option cgi_prefix '/cgi-bin' # CGI 请求的路径前缀
        list lua_prefix '/cgi-bin/luci=/usr/lib/lua/luci/sgi/uhttpd.lua' # 将所有 /cgi-bin/luci 开头的请求交由指定的 Lua 脚本处理,这就是 LuCI 的运行入口。/usr/lib/lua/luci/sgi/uhttpd.lua 是 uHTTPd 专用的 LuCI CGI 启动器
        option script_timeout '3600' # 设置 CGI 脚本最长执行时间为 3600 秒(1 小时),适合处理比较慢的后台任务
        option network_timeout '30' # 客户端连接最长空闲时间(秒)。超过 30 秒没有数据就断开连接
        option http_keepalive '20' # 单个连接最多保持 20 次复用,利于浏览器访问多个资源(JS/CSS)
        option tcp_keepalive '1' # 开启 TCP keepalive,检测空闲连接是否掉线
        option ubus_prefix '/ubus' # 设置 uHTTPd 暴露的 UBUS HTTP API 前缀为 /ubus,用于 RPC 接口
        option interpreter '.sh=/bin/sh' # 定义 .sh 脚本用 /bin/sh 来解释,便于创建简单的 CGI 脚本页面

config cert 'defaults'
        option days '397' # 自签证书有效期 397 天
        option key_type 'ec'
        option bits '256'
        option ec_curve 'P-256'
        option country 'ZZ'
        option state 'Somewhere'
        option location 'Unknown'
        option commonname 'ImmortalWrt'

可以看到服务器文件都放在/www目录下,cgi 的前缀是cgi-bin。因此,为了便捷我我们在/www/cgi-bin目录下新增一个文件wol

wol
#!/bin/sh

# 主机 MAC
mac="A8:DD:77:CC:00:99"
user="mayee"
host="192.168.0.88"

# 从 URL 路径获取参数
p1=$(echo "$PATH_INFO" | sed 's#^/##')
# 获取 GET 参数
p2=$(echo "$QUERY_STRING" | sed -n 's/.*cmd=\([^&]*\).*/\1/p')

# 优先取路径参数,没有则取 GET 参数,还没有就是空
if [ -n "$p1" ]; then
    cmd="$p1"
elif [ -n "$p2" ]; then
    cmd="$p2"
else
    cmd=""
fi

reply(){
  echo "Content-type: text/plain; charset=utf-8"
  echo "" # http 格式要求必须空一行
  echo "$1"
}

remote_cmd() {
    # -i 用来指定私钥的位置
    ssh -i /root/.ssh/id_rsa $user@$host "$1"
}

case "$cmd" in
  ping)
    # -c 发送一次,-t 超时 10ms(内网环境下 ping 不会有高延迟,10ms 足够长了)。成功返回 1,失败返回 0
    fping -c 1 -t 10 $host > /dev/null && reply 1 || reply 0
    ;;
  sleep)
    result=$(remote_cmd trigger_sleep.bat)
    reply $result
    ;;
  wake)
    # -i 用来指定网卡名,参数可选
    etherwake -i br-lan $mac && reply packet snet
    ;;
  shutdown)
    result=$(remote_cmd shutdown /s /f /t 1) # 或者用 shutdown /p
    reply $result
    ;;
  reboot)
    result=$(remote_cmd shutdown /r /f /t 1)
    reply $result
    ;;
  hibernate)
    result=$(remote_cmd trigger_hibernate.bat)
    reply $result
    ;;
  *)
    reply cmd_error: $cmd
    ;;
esac

然后可以通过http://192.168.0.88/cgi-bin/wol/<cmd>访问。cmd 可选值:

  • ping:检测主机是否在线
  • sleep:睡眠
  • hibernate:休眠
  • wake:唤醒
  • shutdown:关机
  • reboot:重启

4.3 IOS 部分

IOS 快捷指令请求 OpenWrt 脚本的逻辑流程如下:

结语

上述这套打法可以轻松实现在局域网内,用 iPhone 手机管理主机。可能你会说只能在局域网中使用还是有点鸡肋啊,能不能不在局域网也可以管理主机呢?当然可以,这也是我们下一篇文章的主题:Lucky内网穿透实践