Files
ykxmz/_build/perf_and_firewall_gate.py
T
xmz7891 b62ac865ea ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性
被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
2026-10-06 19:01:05 +08:00

314 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""任务 A:放行防火墙之前,配对码不加入服务器。
任务 B:性能提升。
────────────────────────────────────────
A. 配对码门禁
────────────────────────────────────────
用户要求:"在放行防火墙之前配对码不会加入服务器"
现在的顺序:
检查码(只读) -> 存码 -> 开自启 -> 放行防火墙 -> run_silent(注册)
虽然注册在防火墙之后,但**防火墙失败也照样注册** ——
服务器上就挂着一个谁也连不上的配对码。
改成:
防火墙没放行成功 -> 不注册,告诉用户
────────────────────────────────────────
B. 性能
────────────────────────────────────────
实测(本机 1920x1080):
抓屏 ImageGrab.grab() 33 ms <- 硬开销,决定了上限
缩放 + JPEG 编码 17 ms (BILINEAR, 960 宽)
合计 50 ms -> 约 20 帧/秒
问题:
1. screen_fps 默认只有 3 —— 白白浪费了 20 帧的能力
2. screen_quality 默认 55 —— 偏糊
3. **画面没变也照抓照编码照发** —— 手表看静态桌面时白烧 CPU 和流量
改动:
1. 默认帧率 3 -> 12
2. 默认质量 55 -> 72
3. 加"画面没变就跳过":用 32x18 缩略图做指纹(很便宜),
指纹一样就不编码、不发送,并把采样间隔逐级拉长(最多 4 倍),
画面一变立刻恢复满帧率。
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
RUN = ROOT / "windows" / "run_agent.py"
CONFIG = ROOT / "windows" / "lanremote" / "config.py"
SCREEN = ROOT / "windows" / "lanremote" / "screen.py"
# ================= A. 配对码门禁 =================
A_OLD = ''' # 黑终端一闪:修防火墙
if result["firewall"]:
fix_firewall_with_flash(store)
'''
A_NEW = ''' # 黑终端一闪:修防火墙
firewall_ok = True
firewall_note = ""
if result["firewall"]:
firewall_ok, firewall_note = fix_firewall_with_flash(store)
# 放行防火墙之前,配对码**不加入服务器**。
#
# 为什么:注册(= 配对码挂到服务器上)如果发生在防火墙之前,
# 那么防火墙没放行成功时,服务器上会留着一个谁也连不上的码,
# 而且它会把码占住,用户重装都换不了。
# 所以这里卡一道:防火墙没成功就不注册。
if not firewall_ok:
_tell_firewall_problem(firewall_note)
return 3
'''
HELPER_ANCHOR = "def _tell_code_problem(message: str) -> None:"
HELPER_NEW = '''def _tell_firewall_problem(detail: str) -> None:
"""防火墙没放行成功,说明为什么这次没生效。"""
message = (
"防火墙没有放行成功,所以这次**没有**把配对码交给服务器。\\n\\n"
f"原因:{detail or '未知'}\\n\\n"
"该怎么办:\\n"
" 1. 右键点 ykxmz.exe →「以管理员身份运行」,重新走一遍设置\\n"
" 2. 或者手动放行:控制面板 → Windows Defender 防火墙 →\\n"
" 允许应用通过防火墙 → 找到 ykxmz → 勾上「专用网络」\\n\\n"
"放行之后再打开 ykxmz.exe 就行了。"
)
try:
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.withdraw()
root.attributes("-topmost", True)
messagebox.showerror("还没生效:防火墙没放行", message)
root.destroy()
return
except Exception: # noqa: BLE001
pass
print("=" * 56)
print("防火墙没放行,配对码未提交到服务器")
print(message)
print("=" * 56)
''' + HELPER_ANCHOR
# ================= B1. 默认参数 =================
CFG_OLD = ''' "screen_quality": 55, # 镜像 JPEG 质量
"screen_fps": 3, # 默认帧率'''
CFG_NEW = ''' # 镜像 JPEG 质量。
# 实测质量对编码耗时几乎没影响(都是 ~17ms),主要影响体积:
# 55 -> 30KB/帧 72 -> 45KB/帧 85 -> 70KB/帧
# 取 72:画质明显更清楚,局域网完全带得动。
"screen_quality": 72,
# 默认帧率。
# 实测抓屏 33ms + 编码 17ms = 50ms,也就是**硬件上限约 20 帧/秒**。
# 原来写 3 是白白浪费了 6 倍的能力。
# 取 12:够顺,又给抓屏留了余量,不至于把 CPU 占满。
# (画面静止时会自动降频,见 screen.py 的 _loop)
"screen_fps": 12,'''
# ================= B2. 抓屏去重 =================
GRAB_OLD = ''' width, height = image.size
if self.max_width and width > self.max_width:
ratio = self.max_width / float(width)
new_size = (self.max_width, max(1, int(height * ratio)))
image = image.resize(new_size, Image.BILINEAR)
width, height = image.size
buffer = io.BytesIO()
try:
image.save(buffer, format="JPEG", quality=self.quality,
optimize=False, subsampling=2)
except Exception as exc: # noqa: BLE001
raise ScreenError(f"JPEG 编码失败: {exc}") from exc
self._last_size = (width, height)
return buffer.getvalue(), width, height'''
GRAB_NEW = ''' width, height = image.size
if self.max_width and width > self.max_width:
ratio = self.max_width / float(width)
new_size = (self.max_width, max(1, int(height * ratio)))
image = image.resize(new_size, Image.BILINEAR)
width, height = image.size
# 画面没变就不编码。
# 用 32x18 的小缩略图做指纹:算一次只要 0.1ms 左右,
# 但能省掉 17ms 的 JPEG 编码和之后的传输。
# 手表看静态桌面是最常见的情况,这一条省得最多。
digest = None
if self.dedup:
try:
thumb = image.resize((32, 18), Image.BILINEAR)
digest = hash(thumb.tobytes())
except Exception: # noqa: BLE001
digest = None
if digest is not None and digest == self._last_digest:
self._last_size = (width, height)
return b"", width, height
self._last_digest = digest
buffer = io.BytesIO()
try:
image.save(buffer, format="JPEG", quality=self.quality,
optimize=False, subsampling=2)
except Exception as exc: # noqa: BLE001
raise ScreenError(f"JPEG 编码失败: {exc}") from exc
self._last_size = (width, height)
return buffer.getvalue(), width, height'''
INIT_OLD = ''' def __init__(self, max_width: int = 480, quality: int = 55, monitor: int = 1):
self.max_width = int(max_width)
self.quality = int(quality)
self.monitor = int(monitor)
self._lock = threading.RLock()
self._last_size = (0, 0)'''
INIT_NEW = ''' def __init__(self, max_width: int = 960, quality: int = 72, monitor: int = 1,
dedup: bool = True):
self.max_width = int(max_width)
self.quality = int(quality)
self.monitor = int(monitor)
# 画面没变就不重复编码(见 grab_jpeg)
self.dedup = bool(dedup)
self._lock = threading.RLock()
self._last_size = (0, 0)
self._last_digest = None'''
# ================= B3. 静态降频 =================
LOOP_OLD = ''' def _loop(self) -> None:
interval = 1.0 / self.fps
while not self._stop.is_set():
started = time.time()
try:
jpeg, width, height = self.capturer.grab_jpeg()
except ScreenError as exc:
self.last_error = str(exc)
time.sleep(0.5)
continue
self.frames_sent += 1
try:
if self.on_frame(jpeg, width, height) is False:
break # 没有观看者了,自动结束
except Exception as exc: # noqa: BLE001
self.last_error = str(exc)
break
elapsed = time.time() - started
wait = interval - elapsed
if wait > 0:
self._stop.wait(wait)'''
LOOP_NEW = ''' def _loop(self) -> None:
interval = 1.0 / self.fps
# 画面静止时把采样间隔逐级拉长(最多 4 倍),省 CPU。
# 抓屏本身就要 30ms 左右,静止时没必要一直满速抓。
idle = 1.0
while not self._stop.is_set():
started = time.time()
try:
jpeg, width, height = self.capturer.grab_jpeg()
except ScreenError as exc:
self.last_error = str(exc)
time.sleep(0.5)
continue
if not jpeg:
# 画面没变(capturer 做了去重):不发送,间隔拉长一点再抓。
idle = min(4.0, idle * 1.6)
self._stop.wait(interval * idle)
continue
idle = 1.0
self.frames_sent += 1
try:
if self.on_frame(jpeg, width, height) is False:
break # 没有观看者了,自动结束
except Exception as exc: # noqa: BLE001
self.last_error = str(exc)
break
elapsed = time.time() - started
wait = interval - elapsed
if wait > 0:
self._stop.wait(wait)'''
# 帧率上限 15 -> 30
CAP_OLD = " self.fps = max(0.5, min(15.0, float(fps)))\n return"
CAP_NEW = " self.fps = max(0.5, min(30.0, float(fps)))\n return"
CAP_OLD2 = " self.fps = max(0.5, min(15.0, float(fps)))\n self._stop.clear()"
CAP_NEW2 = " self.fps = max(0.5, min(30.0, float(fps)))\n self._stop.clear()"
def apply(path: Path, pairs, label: str) -> bool:
text = path.read_text(encoding="utf-8")
ok = True
for old, new in pairs:
if old == new:
continue
if old in text:
text = text.replace(old, new, 1)
else:
print(f" [失败] {label}: 锚点没找到 -> {old.strip()[:60]}")
ok = False
path.write_text(text, encoding="utf-8", newline="\n")
return ok
def main() -> int:
ok = True
# A
text = RUN.read_text(encoding="utf-8")
if "_tell_firewall_problem" in text:
print(" A: 已经改过")
else:
ok &= apply(RUN, [(A_OLD, A_NEW), (HELPER_ANCHOR, HELPER_NEW)], "run_agent")
print(" A: 防火墙门禁已加")
# B1
text = CONFIG.read_text(encoding="utf-8")
if '"screen_fps": 12' in text:
print(" B1: 已经改过")
else:
ok &= apply(CONFIG, [(CFG_OLD, CFG_NEW)], "config")
print(" B1: 默认帧率 3->12,质量 55->72")
# B2 / B3
text = SCREEN.read_text(encoding="utf-8")
if "self._last_digest" in text:
print(" B2/B3: 已经改过")
else:
ok &= apply(SCREEN, [(INIT_OLD, INIT_NEW), (GRAB_OLD, GRAB_NEW),
(LOOP_OLD, LOOP_NEW), (CAP_OLD, CAP_NEW),
(CAP_OLD2, CAP_NEW2)], "screen")
print(" B2: 加了画面去重(指纹 32x18)")
print(" B3: 静止时自动降频(最多 4 倍),帧率上限 15->30")
import ast
for label, path in (("run_agent", RUN), ("config", CONFIG),
("screen", SCREEN)):
try:
ast.parse(path.read_text(encoding="utf-8"))
print(f" {label} 语法 OK")
except SyntaxError as exc:
print(f" {label} 语法错误: 行 {exc.lineno}: {exc.msg}")
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())