Feature: Automatically start frp client for subordinate

This commit is contained in:
yuanyuanxiang
2026-05-21 10:58:00 +02:00
parent da443283f2
commit 5b7d3903b5
7 changed files with 375 additions and 2 deletions

View File

@@ -90,6 +90,7 @@
#define TIMER_PREVIEW_ARRIVAL 8 // 屏幕预览到达超时4 秒未收到则提示"预览不可用"
#define TIMER_PREVIEW_LOOP 9 // "播放快照"循环拉取(间隔由 LOOP_INTERVAL_MS 决定)
#define TIMER_THUMBNAIL_REFRESH 10 // 主机列表缩略图后台刷新(间隔取自 m_ThumbnailCfg
#define TIMER_FRP_CONFIG_CHECK 11 // 检测外部模块对 [settings] FrpConfig 的写入并热切换 FRPC
#define TODO_NOTICE MessageBoxL("This feature has not been implemented!\nPlease contact: 962914132@qq.com", "提示", MB_ICONINFORMATION);
#define TINY_DLL_NAME "TinyRun.dll"
#define FRPC_DLL_NAME "Frpc.dll"
@@ -2186,6 +2187,9 @@ BOOL CMy2015RemoteDlg::OnInitDialog()
#endif
InitFrpClients();
InitFrpcAuto(); // FRP 自动代理(由上级提供配置)
// 记录启动时的 FRP 配置作为基线,由 TIMER_FRP_CONFIG_CHECK 周期性检测外部模块写入的变更
m_lastSeenFrpConfig = THIS_CFG.GetStr("settings", "FrpConfig", "");
SetTimer(TIMER_FRP_CONFIG_CHECK, 10 * 1000, NULL); // 10s 间隔,开销可忽略
UPDATE_SPLASH(90, "正在启动网络服务...");
// 最后启动SOCKET
@@ -2804,6 +2808,14 @@ void CMy2015RemoteDlg::StartFrpcAuto(const FrpAutoConfig& cfg)
THIS_CFG.SetStr("frp_auto", "privilegeKey", cfg.privilegeKey);
THIS_CFG.SetStr("frp_auto", "expireDate", cfg.expireDate);
// 防御性:若已有运行中的 FRPC 线程,先停掉以避免句柄泄露 + 双实例并存。
// 正常调用路径会先 StopFrpcAuto但 InitFrpcAuto 经 [frp_auto] 旧字段启动 +
// 随后外部模块写入 [settings] FrpConfig 的场景可能跳过停步,这里兜底。
if (m_hFrpAutoThread != NULL) {
Mprintf("[FRP-Auto] StartFrpcAuto: 检测到已有运行中的线程,先停止旧实例\n");
StopFrpcAuto();
}
// 启动线程
m_frpAutoStatus = STATUS_UNKNOWN;
m_hFrpAutoThread = CreateThread(NULL, 0, FrpcAutoThreadProc, this, 0, NULL);
@@ -2885,6 +2897,72 @@ void CMy2015RemoteDlg::InitFrpcAuto()
}
}
// 清空 [frp_auto] 节中所有用于自动恢复的字段。
// 必要性InitFrpcAuto 在 [settings] FrpConfig 为空或解析失败时会回退读取 [frp_auto]
// 兼容旧配置,若此处不清理,上级撤销 FRP 后下级一旦重启就会用过期的 privilegeKey
// 重新拉起 FRPC连不上同时还会让上级以为已释放的端口被悄悄占用。
static void ClearFrpAutoSection()
{
THIS_CFG.SetStr("frp_auto", "server", "");
THIS_CFG.SetInt("frp_auto", "serverPort", 0);
THIS_CFG.SetInt("frp_auto", "remotePort", 0);
THIS_CFG.SetStr("frp_auto", "privilegeKey", "");
THIS_CFG.SetStr("frp_auto", "expireDate", "");
}
// 周期性检测 [settings] FrpConfig 是否被外部模块(如授权工具)写入变更。
// 检测到变更后:先停旧 FRPC若有再按新配置启动若非空并在主对话框信息列表中给出友好提示。
// 三种情况均无需重启主程序:
// - 首次(空 → 有StartFrpcAuto
// - 撤销(有 → 空StopFrpcAuto + ClearFrpAutoSection防止重启复活
// - 覆盖(有 → 有值不同StopFrpcAuto + StartFrpcAutoStartFrpcAuto 会重写 [frp_auto]
void CMy2015RemoteDlg::CheckUpperFrpConfigChange()
{
std::string cur = THIS_CFG.GetStr("settings", "FrpConfig", "");
if (cur == m_lastSeenFrpConfig) return;
// 解析(沿用现有 ParseFrpAutoConfig可正确处理含 '-' 的域名)
FrpAutoConfig oldCfg = ParseFrpAutoConfig(m_lastSeenFrpConfig);
FrpAutoConfig newCfg = ParseFrpAutoConfig(cur);
int oldPort = oldCfg.remotePort;
int newPort = newCfg.remotePort;
CString tip;
if (m_lastSeenFrpConfig.empty() && !cur.empty()) {
// 首次:从无到有 → 启动
if (newCfg.enabled) {
StartFrpcAuto(newCfg);
tip.FormatL("[FRP] 已启用上级 FRP 反向代理(远程端口 %d已生效", newPort);
} else {
// 新配置无效,但 cur 非空 —— 提示但不启动;同时确保 [frp_auto] 不残留旧值
ClearFrpAutoSection();
tip.FormatL("[FRP] 收到无效的 FRP 配置: %s", cur.c_str());
}
} else if (!m_lastSeenFrpConfig.empty() && cur.empty()) {
// 撤销:从有到无 → 停止并清空 [frp_auto](否则下次启动会从 [frp_auto] 复活 FRPC
StopFrpcAuto();
ClearFrpAutoSection();
tip = _TR("[FRP] 上级已撤销 FRP 反向代理配置,已停止 FRPC");
} else {
// 覆盖:值变更 → 先停后起
StopFrpcAuto();
if (newCfg.enabled) {
StartFrpcAuto(newCfg); // 内部会用新值覆盖 [frp_auto]
tip.FormatL("[FRP] 上级 FRP 反向代理配置已切换(远程端口 %d → %d已生效",
oldPort, newPort);
} else {
// 新配置无效(解析失败等),旧的 FRPC 已停,[frp_auto] 必须一并清空
ClearFrpAutoSection();
tip.FormatL("[FRP] 收到无效的新 FRP 配置: %s已停止旧 FRPC", cur.c_str());
}
}
Mprintf("[FRP-Auto] %s\n", (LPCSTR)tip);
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg((LPCSTR)tip), NULL);
m_lastSeenFrpConfig = cur;
}
//////////////////////////////////////////////////////////////////////////
void CMy2015RemoteDlg::ApplyFrpSettings()
@@ -3250,6 +3328,9 @@ void CMy2015RemoteDlg::OnTimer(UINT_PTR nIDEvent)
if (nIDEvent == TIMER_STATUSBAR_UPDATE) {
UpdateStatusBarStats();
}
if (nIDEvent == TIMER_FRP_CONFIG_CHECK) {
CheckUpperFrpConfigChange();
}
if (nIDEvent == TIMER_STATUSBAR_INIT) {
KillTimer(TIMER_STATUSBAR_INIT); // 只执行一次
// 强制重新计算状态栏分区宽度
@@ -3544,6 +3625,7 @@ void CMy2015RemoteDlg::Release()
#ifdef _WIN64
StopLocalFrpsServer(); // 停止本地 FRPS 服务器
#endif
KillTimer(TIMER_FRP_CONFIG_CHECK); // 必须先于 StopFrpcAuto避免末班定时器再次起飞 FRPC
StopFrpcAuto(); // 停止 FRP 自动代理
THIS_APP->Destroy();