16 KiB
Gateway Keepalive:.NET 托盘版重写方案
日期:2026-10-23。本文是待实施方案。
源码基线:main,a18914bd4e38feb2984398b7dc3619e794366697(提交说明:重构网关切换逻辑,待测试)。已通过本机 lion-desktop SSH 密钥克隆。此次仅阅读源码、验证独立的参数绑定问题并编写方案,未运行原保活脚本,未修改系统网络设置。
1. 目标与推荐选型
做成一个 Windows 常驻托盘工具:检测旁路由是否可用,故障时切回主路由,恢复稳定后切回旁路由;可以看状态、手动指定模式、暂停自动切换和恢复网络配置。
推荐 C# + .NET 10 LTS + WinForms,目标框架 net10.0-windows,第一版面向 x64,支持平台以 .NET 10 官方 Windows 支持矩阵为准。用 ApplicationContext + NotifyIcon 管理常驻生命周期,参考隔壁鼠标工具的深色弹出面板、自绘状态图标、悬停气泡和单实例唤醒。
隔壁工具使用 .NET Framework 4.8 和系统 csc,优点是产物小、无需另装运行时。本项目优先使用现代 .NET 的异步网络、取消与测试支持;不以它的 82 KB 作为体积目标。发布提供两种包:本机已有 Desktop Runtime 时使用较小的依赖运行时版本,其他机器使用包含运行时的独立版本。单 exe 的原生依赖打包与解包行为需在发布阶段验证,第一版不启用裁剪或 Native AOT。
规划时本机已有 .NET 10 Desktop Runtime,尚未安装 SDK。实施阶段已在用户目录准备并校验 .NET 10 SDK;实现与验证结果见 docs/validation.md。
2. 原项目的实际行为
| 文件 | 作用 |
|---|---|
GateWay-KeepAlive.ps1 |
提权、选择网卡、读取和切换默认路由、设置 DNS、运行健康检查循环 |
proxy-test.ps1 |
SOCKS5 无认证握手与目标 CONNECT 探测 |
config.ps1 |
网关、网卡关键字、探测与防抖参数 |
logger.ps1 |
调试控制台输出与 Windows 事件日志 |
现有配置:主路由 192.168.6.1,旁路由 192.168.6.2,SOCKS5 192.168.6.2:1070;旁路由 DNS 也使用 .2;目标 www.google.com:443;网卡名称包含 Ethernet;每轮检查结束后等待 5 秒;连续成功/失败 3 次,切换冷却 30 秒。直连 DNS 配置为空时重置为自动获取。
当前属于通过更换默认网关接入旁路由的方案,不是设置 Windows 系统代理。SOCKS5 只是旁路由能力探针;SOCKS 可用不能单独证明经旁路由转发的默认路径可用。实际故障反应时间还要加上探测耗时和冷却剩余时间,不能简单承诺为 15 秒。
3. 重写时必须处理的问题
| 源码现状 | 新版处理 |
|---|---|
Test-HostReachable 使用参数 $Host,与 PowerShell 只读自动变量冲突 |
改成 C# 探针;已用独立参数绑定示例确认会报只读变量错误,无需执行原脚本 |
| 先删除目标网卡所有 IPv4 默认路由,再添加新网关 | 只操作明确归属的路由,分阶段切换、读回验证、失败补偿,保留原路径到切换成功 |
| DNS 与路由分开写入,出错没有回滚 | 保存切换前快照与操作日志;任何一步失败都进入补偿流程,恢复失败单独告警 |
直连 DNS 恢复默认按 DHCP,未保存原始静态设置;originalDns、TestIP 没进入实际检查链 |
区分自动 DNS 与有序静态 DNS 列表;恢复真实基线;合并配置定义与实际使用 |
| 根据是否等于旁路由网关判为代理/直连,第三方网关也会算直连 | 识别主路由、旁路由、外部配置、无路由和网卡断开,不擅自覆盖外部配置 |
| 仅按网卡名称取第一个 Up 网卡,循环固定旧索引 | 首次显式选择并保存网卡 GUID;索引随枚举更新;网络环境变化重新校验 |
| SOCKS 响应假设一次 Read 足够,连接回复固定读 10 字节 | 精确读取、检查版本/方法/返回码,按 IPv4、域名、IPv6 地址类型消费完整回复 |
| 依赖 ICMP 且部分探测缺少统一总超时 | ICMP 默认只做诊断;网络操作均有超时、整轮预算与取消 |
| DNS/SOCKS 成功后还可能默认路径不通,探测也可能经另一张网卡出网 | 分开显示各层结果;校验目标网卡与有效路由,必要时绑定探测源地址,切换后验证实际转发路径 |
4. 托盘交互
左键托盘图标开关面板;点外部或按 Esc 收起。首次启动打开配置向导,后续启动只驻留托盘。关闭面板继续工作;“退出程序”才结束常驻。
主面板约 400 × 430 逻辑像素,最终尺寸以中文排版实测为准:
网关保活 自动 · 旁路由
当前网关 192.168.6.2 检测正常
Ethernet · 当前 DNS 192.168.6.2
旁路由连接 正常 2 ms
旁路由 DNS 正常 12 ms
SOCKS5 正常 180 ms
当前出口 正常 220 ms
[ 自动 ] [ 固定直连 ] [ 固定旁路由 ]
连续失败 0/3 · 距上次切换 2 分钟
最近事件:旁路由已恢复,自动切回
[立即检测] [暂停自动切换] [设置] [日志]
状态图标同时使用颜色和形状/标记:绿色表示旁路由工作,蓝色表示主路由直连,黄色表示降级或切换中,红色表示网络故障,灰色表示暂停或目标网卡断开。悬停只显示当前模式、网关及最近异常,不启动额外网络检测。
右键菜单:打开面板、立即检测、模式选择、暂停/继续自动切换、恢复接管前配置、登录后启动、查看日志、退出。设置使用独立窗口,包含网卡、主/旁路由、DNS 策略、探测目标与端口、阈值/间隔/冷却、提醒和退出行为。
“暂停”仅停止自动改配置,仍展示网络状态;固定模式完成一次指定切换后持续监测与告警,不被自动恢复逻辑覆盖。恢复接管前配置后进入暂停,防止马上又被自动切走。默认退出停止监测并保留当前网络,提供可选的“退出时恢复接管前配置”,界面明确显示该行为。
通知只在模式真正变化、故障持续、回滚失败等事件发生时提示,相同状态不每 5 秒弹一次。
5. 后台状态机与健康检查
把“用户策略”和“实际网络状态”分开保存。
- 用户策略:
Auto、ForceDirect、ForceProxy、Paused。 - 网络状态:
Initializing、Direct、Proxy、Switching、Degraded、Disconnected、ExternalConfiguration、RecoveryRequired。
使用一个异步控制循环串行处理定时检查、立即检测、手动切换、配置更新和网卡变化事件。探针可在预算内并行,但路由/DNS 写入始终串行;立即检测请求合并,避免重复点击产生多个检查循环。通过状态快照推送到 UI 线程,窗体事件不直接改网络。
自动模式保留默认的 5 秒间隔、成功/失败 3 次和 30 秒冷却,均可配置。成功与失败计数分别要求连续;进入新环境或切换完成后重置;冷却用单调时间计算,系统时间校正不影响它。失败回退和恢复旁路由可以配置不同冷却,避免恢复后的长冷却拖延紧急回退。网卡断开不是旁路由故障,断开期间停止累计与切换。
健康检查分层记录耗时与失败原因:旁路由 TCP 可达、指定旁路由 DNS 查询、可选 SOCKS5 CONNECT、当前默认路径 HTTPS/TLS。DNS 查询明确指定 DNS 服务器,不能用只走系统 DNS 的 Dns.GetHostAddressesAsync 冒充指定服务器检测。优先封装 Windows DNS API;SOCKS5 使用 TcpClient/NetworkStream 自行实现精确帧读取。
旁路由恢复候选检查即使处于直连也可以运行。切回旁路由后必须验证实际出口;未通过就回退并进入恢复退避,避免“SOCKS 好用、默认转发坏了”造成循环切换。支持多个可配置探测目标及成功策略,减少单站点故障误判;第一版默认沿用现有目标,同时在配置中明确展示它。
初始每探针超时建议 1.5~3 秒,整轮预算建议 4 秒,以实测调整。超过预算记一次失败并释放 socket;下轮不与本轮重叠,不在恢复后补跑积压轮次。休眠唤醒、DHCP 更新、网络改变时清零旧计数、等待环境稳定后重新枚举。
6. 网络接管、回滚与权限
通过 Windows IP Helper API 操作 IPv4 默认路由:GetIpForwardTable2、GetIpInterfaceEntry、CreateIpForwardEntry2、SetIpForwardEntry2、DeleteIpForwardEntry2,用 GetBestRoute2 验证有效选路;通过 GetInterfaceDnsSettings / SetInterfaceDnsSettings 读取和设置 DNS。所有结构布局、字符串生命周期、返回码与原生内存释放封装在 Windows 层。DNS API 的最低系统版本为 Windows 10 Build 19041,程序同时检查运行时平台要求。
切换流程:
- 验证目标网卡 GUID、当前网络指纹、网关可达范围、当前路由/DNS 与上次快照是否一致。
- 原子保存本次操作前快照与切换日志,包含路由 metric、接口 metric、DNS 自动/静态来源与有序列表。
- 准备目标路由,优先保留原路由,通过精确路由项与 metric 控制优先级,考虑全机其他接口的有效路由。
- 按目标模式应用 DNS 与路由优先级,读回验证两者,执行出口检查;系统没有跨路由和 DNS 的原子事务,界面显示切换中。
- 成功才发布新状态和记录冷却;失败按操作日志恢复刚才改过的项,读回验证;补偿失败停止自动切换并展示恢复操作。
第 3 步的 metric 策略需先做原生 API 原型验证:DHCP 路由可能被系统重建,不能靠假设永久提高一次 metric 就一直有效;不能影响 VPN、其他网卡、特定前缀路由或 IPv6。第一版只管理选定网卡的 IPv4 默认路径,面板明确标注这个范围,不能把 IPv6 绕行误报为 IPv4 代理成功。
“切换前快照”与“接管前直连基线”分开保存。若启动时已处于旁路由,不能把当前旁路由误存成直连基线;优先使用同一网络且验证过的历史直连基线,或要求配置主路由与 DNS 策略。不同网络环境不重放旧快照。发现程序外修改时进入外部配置状态,暂停写入并允许重新接管。
路由写入需要管理员权限。第一版采用一个管理员权限托盘进程,启动时提权,拒绝提权则明确退出,不启动半工作的自动切换。登录自启使用任务计划程序,以当前用户、交互式登录、最高权限运行;首次创建/修改该计划在已提权进程中完成。不能直接套用鼠标工具的 HKCU Run 启动方式,也不使用无交互 Session 0 运行托盘。
不在第一版额外引入 Windows 服务。以后若需要未登录也保活,再拆成低权限托盘 + 管理员服务;现有 Core/Windows 边界便于迁移。崩溃后程序本身无法立即回滚,第一版靠持久化日志在下次启动时识别未完成切换,验证现场后进入恢复流程,不能承诺进程退出后仍有实时恢复能力。
7. 工程划分与数据
GatewayKeepalive.sln
src/
GatewayKeepalive.Core/ 配置模型、状态机、切换计划、探针/时钟/系统接口
GatewayKeepalive.Windows/ 网卡枚举、路由/DNS API、权限、任务计划
GatewayKeepalive.Tray/ Program、ApplicationContext、托盘、面板、设置、日志
tests/
GatewayKeepalive.Tests/ 状态机、故障注入、SOCKS5 假服务、配置迁移
docs/
network-behavior.md 网络修改范围、恢复语义、手动恢复步骤
Core 不依赖 WinForms。关键接口包括 INetworkConfiguration、IHealthProbe、ISnapshotStore 和可替换时钟;用假实现验证切换,不依赖真实网关。
配置放 %LocalAppData%\GatewayKeepalive\settings.json,保留 schemaVersion、网卡 GUID、网关、DNS 策略、探测与 UI 设置;快照和未完成切换日志独立保存。所有持久化采用临时文件写入再替换、校验与必要备份。按天滚动文件日志记录探测摘要、切换原因、系统错误码和恢复结果,设置保留期限;Windows 事件日志作为可选集成。
原 config.ps1 保留为历史参考。迁移只映射明确支持的赋值项,遇到表达式则让用户手动填写;不通过执行 PowerShell 配置来导入。首次展示原配置中的 .1、.2、1070 和阈值供确认,不能仅凭名称猜中网卡就开始改网络。
参考隔壁的 TrayApp.cs、PanelForm.cs、Theme.cs、DarkForm.cs、TextTip.cs、TrayIconRenderer.cs 和单实例交互;保留体验,按现代 WinForms 生命周期重构。使用 PerMonitorV2 和随目标显示器 DPI 更新布局,避免直接照搬隔壁的系统 DPI 固定缩放;悬停定时器仅气泡显示时运行。
8. 实施顺序与验收
| 阶段 | 交付与验收 |
|---|---|
| 1:只读诊断 | SDK/解决方案、网卡和路由/DNS快照、分层探针、--diagnose。不写系统设置,展示实际选路与失败原因 |
| 2:可恢复切换 | 原生 API 原型、手动切换、操作日志与补偿、--dry-run。故障注入验证任一步失败均不冒报成功;真实双网关环境验证读回和回退 |
| 3:自动保活 | 状态机、防抖、冷却、恢复退避、网络变更/唤醒处理。验证 2 次失败不切、3 次达阈值才切、冷却阻止抖动、固定/暂停策略不被覆盖 |
| 4:托盘成品 | 面板、设置、单实例、图标/气泡、滚动日志、登录自启与发布。验证 100%/150%/200% 缩放、混合 DPI 多屏、Explorer 重启、退出语义与注销行为 |
有价值的自动测试:用假时钟检验计数与冷却;在路由成功/DNS失败、DNS成功/出口失败、补偿失败等边界注入故障;本地 SOCKS5 假服务器分片、截断、延迟、返回不同地址类型;验证旧快照不会跨网卡/跨网络恢复。测试不修改开发机真实网络。
真机集成覆盖旁路由关闭、DNS服务停止、SOCKS端口关闭、默认转发失效但SOCKS正常、目标站点单独故障、拔网线、DHCP续租、VPN共存、双网卡、重启程序时仍在旁路由模式。第一阶段完成后再进入真实网络切换验收;当前没有实际测量性能、执行故障演练或构建新版。
第一版完成标准:平时托盘常驻;自动回退/恢复原因清楚;手动模式稳定;任何写入失败都有可验证的恢复结果;未知网络不自动套用旧配置;能够从日志解释每一次切换。
9. 官方技术依据
- .NET 支持策略:.NET 10 为 LTS,支持至 2028 年 11 月。
- WinForms NotifyIcon:托盘图标、菜单与后台进程交互。
- SetIpForwardEntry2:管理员要求、路由项键与路由/接口 metric 的关系。
- GetInterfaceDnsSettings / SetInterfaceDnsSettings:接口 DNS 读写与最低系统版本。
- 任务计划的安全上下文:交互式登录与最高权限注册要求。
- .NET 单文件部署:依赖运行时/独立部署、原生依赖解包与体积取舍。