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 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
This commit is contained in:
xmz7891
2026-10-06 19:01:05 +08:00
commit b62ac865ea
3195 changed files with 203292 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
# -*- coding: utf-8 -*-
"""全项目编码审计(收窄版)。
上一版太吵:把 Java 字节码、测试用的 .bin、还有审计脚本自己
(源码里就写着乱码样本)全当成文本查了。
这一版:
* 用**内容**判断是不是二进制(不是只看扩展名)
* 跳过编译产物和测试数据目录
* 跳过审计脚本自己
只查真正要交付和要跑的东西。
"""
from __future__ import annotations
import sys
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SELF = Path(__file__).resolve()
# 这些目录是产物/数据,不是源码
SKIP_DIRS = {
"_build", "__pycache__", ".git", "dist_exe", "dist", "pyinstaller_work",
"node_modules", "out_java", "received", "gen", "javacheck", "javacheck2",
"javacheck3", "javacheck4", "javacheck5", "build_tmp", "toolchain",
}
SKIP_SUFFIX = {
".pyc", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".apk", ".exe", ".dll",
".pyd", ".zip", ".idsig", ".so", ".class", ".bin", ".jar", ".keystore",
".jks", ".der", ".pem", ".7z",
}
MOJIBAKE = [
"锟斤拷", "�", "â€", "ä", "é", "è", "а", "Ñ€",
"\ufffd",
"ä¸", "å¤", "æµ", "è¯", "é€",
]
# 只查这些扩展名(其余一律不问)
TEXT_SUFFIX = {".py", ".java", ".ps1", ".sh", ".bat", ".cmd", ".txt",
".md", ".json", ".yml", ".yaml", ".xml", ".gradle", ".cfg",
".ini", ".properties", ".pro", ".service", ".timer"}
problems = []
stats = {}
scanned = 0
def report(level: str, path, message: str) -> None:
label = str(path.relative_to(ROOT)) if isinstance(path, Path) \
and str(path).startswith(str(ROOT)) else str(path)
problems.append((level, label, message))
def looks_binary(body: bytes) -> bool:
if b"\x00" in body[:4096]:
return True
# 非文本字节比例过高就当二进制
sample = body[:4096]
if not sample:
return False
weird = sum(1 for byte in sample
if byte < 8 or (14 <= byte < 32 and byte != 27))
return weird / len(sample) > 0.10
def check_bytes(path, name: str, suffix: str, raw: bytes) -> None:
"""对一份内容做编码检查。path 只用于报错定位。"""
global scanned
scanned += 1
stats[suffix] = stats.get(suffix, 0) + 1
has_bom = raw.startswith(b"\xef\xbb\xbf")
body = raw[3:] if has_bom else raw
# .bat 走特殊逻辑:cmd.exe 在中文 Windows 上默认代码页 936,
# 所以 .bat 里要写中文就必须是 **GBK**(UTF-8 的中文会变乱码)。
# 纯 ASCII 的 .bat 两种代码页下都对,最保险。
if suffix in (".bat", ".cmd"):
if has_bom:
report("警告", path, f"{name} 有 BOM(cmd 会把 BOM 当命令)")
try:
text = body.decode("gbk")
encoding_name = "GBK"
except UnicodeDecodeError:
try:
text = body.decode("utf-8")
if any("\u4e00" <= ch <= "\u9fff" for ch in text):
report("严重", path,
f"{name} 是 UTF-8 且含中文 —— cmd.exe 下会乱码,应为 GBK")
encoding_name = "UTF-8(纯ASCII)"
except UnicodeDecodeError as exc:
report("严重", path, f"{name} GBK/UTF-8 都解不开:{exc}")
return
crlf = body.count(b"\r\n")
lf_only = body.count(b"\n") - crlf
if lf_only:
report("警告", path, f"{name} 有 {lf_only} 处纯 LF(.bat 应为 CRLF)")
stats[suffix] = stats.get(suffix, 0) + 1
scanned += 1
return
try:
text = body.decode("utf-8")
except UnicodeDecodeError as exc:
report("严重", path, f"{name} 不是合法 UTF-8:{exc}")
return
if suffix == ".ps1" and not has_bom:
report("严重", path, f"{name} 缺 UTF-8 BOM(PowerShell 5.1 会按 GBK 读)")
if suffix in (".py", ".java", ".sh") and has_bom:
report("警告", path, f"{name} 不应有 BOM")
if suffix in (".bat", ".cmd") and has_bom:
report("警告", path, f"{name} 不应有 BOM")
for token in MOJIBAKE:
if token in text:
line = next((i + 1 for i, l in enumerate(text.splitlines())
if token in l), 0)
report("严重", path, f"{name} 疑似乱码 {token!r}(第 {line} 行)")
break
crlf = body.count(b"\r\n")
lf_only = body.count(b"\n") - crlf
cr_only = body.count(b"\r") - crlf
if suffix == ".sh" and (crlf or cr_only):
report("严重", path, f"{name} 有 CR(CRLF {crlf} 处)—— Linux 上跑不起来")
if suffix in (".bat", ".cmd") and lf_only:
report("警告", path, f"{name} 有 {lf_only} 处纯 LF")
if cr_only:
report("警告", path, f"{name} 有 {cr_only} 处孤立 CR")
if crlf and lf_only and suffix not in (".bat", ".cmd"):
report("提示", path, f"{name} 行尾混用(CRLF {crlf} / LF {lf_only})")
def check_zip(zip_path: Path) -> None:
if not zip_path.is_file():
return
print(f" 交付包:{zip_path.name}")
with zipfile.ZipFile(zip_path) as archive:
for name in archive.namelist():
if name.endswith("/"):
continue
suffix = Path(name).suffix.lower()
if suffix not in TEXT_SUFFIX:
continue
raw = archive.read(name)
if not raw or looks_binary(raw):
continue
check_bytes(zip_path, f"包内 {name}", suffix, raw)
def main() -> int:
print("=" * 74)
print(" 编码审计(只查要交付 / 要跑的文本文件)")
print("=" * 74)
targets = []
for path in ROOT.rglob("*"):
if not path.is_file() or path.resolve() == SELF:
continue
rel = path.relative_to(ROOT)
if set(rel.parts) & SKIP_DIRS:
continue
if path.suffix.lower() in SKIP_SUFFIX:
continue
if path.suffix.lower() not in TEXT_SUFFIX:
continue
targets.append(path)
print(f" 源码目录扫描 {len(targets)} 个文本文件…")
for path in sorted(targets):
raw = path.read_bytes()
if not raw or looks_binary(raw):
continue
check_bytes(path, path.name, path.suffix.lower(), raw)
print()
check_zip(ROOT / "dist" / "ykxmz-1.4.0.zip")
print()
print(" 按类型统计:")
for suffix, count in sorted(stats.items(), key=lambda x: -x[1]):
print(f" {suffix:<12} {count:>5} 个")
print()
print("=" * 74)
if not problems:
print(" ✓ 全部通过,没有编码问题")
return 0
by_level: dict = {}
for level, path, message in problems:
by_level.setdefault(level, []).append((path, message))
for level in ("严重", "警告", "提示"):
items = by_level.get(level, [])
if not items:
continue
print(f" 【{level}】{len(items)} 条")
for path, message in items[:20]:
print(f" {path}")
print(f" {message}")
if len(items) > 20:
print(f" …还有 {len(items) - 20} 条")
print()
print(f" 合计 {len(problems)} 条")
return 1 if by_level.get("严重") else 0
if __name__ == "__main__":
sys.exit(main())