mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 18:20:43 +00:00
被控端 - 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%) - 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败) - 每分钟回头找局域网,自动切回快 400 倍的路径 - 画面心跳帧:静止时省流量,客户端仍知道流是活的 - 锁屏时给出可操作的提示 手表端 - 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException - 连接错误翻译成人话(证书/时间/解析/超时分别提示) - 经中转连上后用服务器报的地址刷新已配对记录 - 新增局域网连接页面、服务器延迟检测 服务器端 - 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502) - 网络调优脚本 net-tune.sh(开机自动应用) - 管理页:批量删除、网络体检、下载区 - 隧道改 HTTP/2 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
222 lines
7.8 KiB
Python
222 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""统一打包:被控端 + 手表端(已内置诊断)+ 文档。
|
||
|
||
注意:遥控诊断原来是一个单独的 App,现在已合并进手表主程序
|
||
(主界面 ->「设备诊断」按钮),所以不再单独出 APK。
|
||
|
||
产物(都在 dist/):
|
||
ykxmz-Windows.zip 被控端(解压即用,内含工具箱)
|
||
LanRemote-Watch-1.3.0.apk 手表控制端(含设备诊断)
|
||
LanRemote-1.3.0-完整包.zip 以上全部 + 预览图 + 文档
|
||
|
||
被控端目录结构(解压后):
|
||
LanRemote-Agent/
|
||
├── (已移除) <- 双击这个,所有功能都在菜单里
|
||
├── ykxmz.exe
|
||
├── _internal/
|
||
├── xffhq.bat
|
||
├── wlzj.bat / .py
|
||
├── 使用说明.txt
|
||
└── 先看这个.txt
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import shutil
|
||
import sys
|
||
import zipfile
|
||
from pathlib import Path
|
||
|
||
VERSION = "1.4.0"
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
DIST = ROOT / "dist"
|
||
EXE_DIR = ROOT / "dist_exe" / "ykxmz"
|
||
TOOLS = ROOT / "tools"
|
||
PREVIEW = ROOT / "dist_preview"
|
||
README = ROOT / "README.md"
|
||
TEST_REPORT = DIST / "测试报告.txt"
|
||
|
||
APK = DIST / f"LanRemote-Watch-{VERSION}.apk"
|
||
AGENT_ZIP = DIST / "ykxmz-Windows.zip"
|
||
BUNDLE = DIST / f"LanRemote-{VERSION}-完整包.zip"
|
||
|
||
# 会被放进被控端目录的工具(工具箱必须排第一)
|
||
TOOL_FILES = (
|
||
"fxfhq.bat",
|
||
"wlzj.bat",
|
||
"xffhq.bat",
|
||
)
|
||
|
||
QUICKSTART = f"""========================================================
|
||
局域网遥控 · 被控端(Windows) 版本 {VERSION}
|
||
========================================================
|
||
|
||
*** 不需要装 Python ***
|
||
这个目录里的 ykxmz.exe 是编译好的独立程序,
|
||
拷到任何一台 Windows 电脑上双击就能跑。
|
||
|
||
【装完之后怎么用】
|
||
|
||
1) 双击 ykxmz.exe
|
||
|
||
2) 弹出一个小窗口,让你输入 6 位数字(配对码)
|
||
自己定一个记得住的,比如 789113
|
||
下面有两个勾:
|
||
[x] 设置开机自启(开机后自动在后台运行)
|
||
[x] 顺便放行防火墙(需要管理员确认一次)
|
||
|
||
3) 点「确定,开始使用」
|
||
→ 会弹一次 Windows 的管理员确认(UAC),点「是」
|
||
→ 黑色终端窗口一闪,它在一键修复防火墙
|
||
→ 窗口全部消失,程序转入后台静默运行
|
||
|
||
4) 手表上打开 App → 点「自动搜索电脑」→ 点搜到的电脑
|
||
→ 输入你设的 6 位数字
|
||
|
||
5) 以后开机就直接在后台跑,屏幕上什么都没有;
|
||
手表打开 App 会自动连上,数字都不用再输。
|
||
|
||
|
||
【目录里这几个文件】
|
||
|
||
ykxmz.exe 主程序。双击就是上面第 1 步。
|
||
wlzj.bat 网络自检。连不上时跑它,看手表的包到底有没有到电脑。
|
||
xffhq.bat 一键修复防火墙。右键 →「以管理员身份运行」。
|
||
fxfhq.bat 同上,是程序自动生成的放行脚本,也可以右键提权运行。
|
||
先看这个.txt / 使用说明.txt 就是本文。
|
||
|
||
三个 .bat 都不需要装 Python。
|
||
|
||
|
||
【连不上?按这个顺序来】
|
||
|
||
1) 确认被控端在跑
|
||
任务管理器里能看到 ykxmz.exe
|
||
没有的话:ykxmz.exe --setup
|
||
|
||
2) 右键 xffhq.bat →「以管理员身份运行」,点「是」
|
||
|
||
3) 还不行 → 双击 wlzj.bat(网络自检)
|
||
它会监听 90 秒,看手表的请求有没有到达这台电脑:
|
||
收到连接 -> 网络是通的,问题在电脑这边
|
||
一直没动静 -> 包没到电脑,多半路由器开了「AP 隔离」
|
||
|
||
4) 手表上:首页「连不上?点这里诊断」
|
||
|
||
|
||
【命令行参数】(一般用不到)
|
||
|
||
ykxmz.exe --setup 重新打开设置窗口
|
||
ykxmz.exe --silent 直接静默运行
|
||
ykxmz.exe --add-device 10 静默运行 + 开放 10 分钟配对
|
||
ykxmz.exe --autostart on|off 开关开机自启
|
||
ykxmz.exe --status 看当前状态
|
||
ykxmz.exe --netcheck 90 网络自检(同 wlzj.bat)
|
||
ykxmz.exe --fix-firewall 提权修复防火墙
|
||
ykxmz.exe --reset-devices 清空已配对设备
|
||
|
||
|
||
【关于安全】
|
||
|
||
配对码是你自己设的,只存在这台电脑的 data\\settings.json 里。
|
||
一台设备都没配对过时配对是开着的(否则你设完码会连不上自己);
|
||
第一台配对成功后自动关闭。
|
||
配错 10 次锁 5 分钟(按来源 IP 分别计,别人锁不住你)。
|
||
|
||
屏幕共享时右下角会出现一条红色小提示 ——
|
||
这是故意的:有人在看你的屏幕时,屏幕前面的人应该知道。
|
||
"""
|
||
|
||
|
||
def clean_runtime_state() -> None:
|
||
"""打包前清掉运行时产生的东西,保持交付干净。"""
|
||
for name in ("data", "received"):
|
||
target = EXE_DIR / name
|
||
if target.exists():
|
||
shutil.rmtree(target, ignore_errors=True)
|
||
print(f" 已清理 {target.relative_to(ROOT)}")
|
||
for stale in ("fxfhq.bat",):
|
||
path = EXE_DIR / stale
|
||
if path.exists():
|
||
path.unlink()
|
||
print(f" 已清理 {stale}")
|
||
|
||
|
||
def install_tools() -> list[str]:
|
||
installed = []
|
||
for name in TOOL_FILES:
|
||
source = TOOLS / name
|
||
if source.is_file():
|
||
shutil.copy2(source, EXE_DIR / name)
|
||
installed.append(name)
|
||
else:
|
||
print(f" [警告] 缺少工具: {name}")
|
||
return installed
|
||
|
||
|
||
def zip_dir(source: Path, target: Path, arc_prefix: str = "") -> int:
|
||
files = [p for p in source.rglob("*") if p.is_file()]
|
||
with zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
|
||
for path in files:
|
||
rel = path.relative_to(source)
|
||
arc = str(Path(arc_prefix) / rel) if arc_prefix else str(rel)
|
||
zf.write(path, arc)
|
||
print(f" {target.name} ({target.stat().st_size / 1048576:.1f} MB, {len(files)} 个文件)")
|
||
return len(files)
|
||
|
||
|
||
def main() -> int:
|
||
missing = []
|
||
if not EXE_DIR.is_dir():
|
||
missing.append(f"被控端产物目录 {EXE_DIR}(先运行 windows\\build_exe.ps1)")
|
||
if not APK.is_file():
|
||
missing.append(f"{APK.name}(先运行 android\\build_apk.ps1)")
|
||
if missing:
|
||
print("缺少产物:")
|
||
for item in missing:
|
||
print(f" - {item}")
|
||
return 1
|
||
|
||
DIST.mkdir(parents=True, exist_ok=True)
|
||
|
||
print("清理测试残留:")
|
||
clean_runtime_state()
|
||
|
||
print("\n安装工具到被控端目录:")
|
||
installed = install_tools()
|
||
for name in installed:
|
||
print(f" + {name}")
|
||
|
||
(EXE_DIR / "使用说明.txt").write_text(QUICKSTART, encoding="utf-8")
|
||
(EXE_DIR / "先看这个.txt").write_text(QUICKSTART, encoding="utf-8")
|
||
print(" + 使用说明.txt / 先看这个.txt")
|
||
|
||
print("\n打包被控端:")
|
||
count = zip_dir(EXE_DIR, AGENT_ZIP, arc_prefix="ykxmz")
|
||
|
||
print("\n打包完整交付:")
|
||
with zipfile.ZipFile(BUNDLE, "w", zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
|
||
zf.writestr("先看这个.txt", QUICKSTART)
|
||
zf.write(APK, f"1-手表控制端/{APK.name}")
|
||
zf.write(AGENT_ZIP, f"2-电脑被控端/{AGENT_ZIP.name}")
|
||
if PREVIEW.is_dir():
|
||
for shot in sorted(PREVIEW.glob("*.png")):
|
||
zf.write(shot, f"3-界面预览/{shot.name}")
|
||
if README.is_file():
|
||
zf.write(README, "使用说明.md")
|
||
if TEST_REPORT.is_file():
|
||
zf.write(TEST_REPORT, "测试报告.txt")
|
||
print(f" {BUNDLE.name} ({BUNDLE.stat().st_size / 1048576:.1f} MB)")
|
||
|
||
print("\n" + "=" * 62)
|
||
print(f"交付内容(版本 {VERSION}):")
|
||
for path in (APK, AGENT_ZIP, BUNDLE):
|
||
print(f" {path.stat().st_size / 1048576:7.2f} MB {path.name}")
|
||
print(f"\n被控端包内含 {count} 个文件,工具箱等 {len(installed)} 个工具已就位。")
|
||
print("遥控诊断已合并进手表主程序,不再单独出包。")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|