Files
Gateway-keepaliveForDotnet/PLAN.md
T
chuzhongzai 2d85527656 实现 .NET 10 托盘版网关保活
包含分层探测、可补偿的 IPv4 路由/DNS 切换、自动策略、托盘与分组设置界面、只读诊断工具、离线测试和发布脚本。
2026-10-23 23:35:20 +08:00

16 KiB
Raw Blame History

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,程序同时检查运行时平台要求。

切换流程:

  1. 验证目标网卡 GUID、当前网络指纹、网关可达范围、当前路由/DNS 与上次快照是否一致。
  2. 原子保存本次操作前快照与切换日志,包含路由 metric、接口 metric、DNS 自动/静态来源与有序列表。
  3. 准备目标路由,优先保留原路由,通过精确路由项与 metric 控制优先级,考虑全机其他接口的有效路由。
  4. 按目标模式应用 DNS 与路由优先级,读回验证两者,执行出口检查;系统没有跨路由和 DNS 的原子事务,界面显示切换中。
  5. 成功才发布新状态和记录冷却;失败按操作日志恢复刚才改过的项,读回验证;补偿失败停止自动切换并展示恢复操作。

第 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. 官方技术依据