mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 17:10: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 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
206 lines
8.1 KiB
Python
206 lines
8.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""应用内网络自检(原来是一个独立的控制台脚本,现在做进界面里)。
|
||
|
||
和独立脚本最大的区别:
|
||
被控端自己就在监听 8765/8766,所以**不需要再去绑端口**,
|
||
直接读它自己的计数器就知道手表的包有没有到:
|
||
* 自动发现请求收到几个
|
||
* TCP 连接来过几次
|
||
这比再开一个进程抢端口靠谱得多,也正是"连不上"时最想知道的事。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import platform
|
||
import socket
|
||
import subprocess
|
||
import time
|
||
|
||
from .config import app_dir
|
||
from .discovery import DISCOVERY_PORT
|
||
from .firewall import check as firewall_check
|
||
|
||
_NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0)
|
||
|
||
|
||
def _run(cmd: list[str], timeout: int = 12) -> str:
|
||
try:
|
||
proc = subprocess.run(cmd, capture_output=True, timeout=timeout,
|
||
creationflags=_NO_WINDOW,
|
||
encoding="mbcs" if os.name == "nt" else "utf-8",
|
||
errors="replace")
|
||
return (proc.stdout or "") + (proc.stderr or "")
|
||
except Exception: # noqa: BLE001
|
||
return ""
|
||
|
||
|
||
def local_ips() -> list[tuple[str, str]]:
|
||
"""本机所有可用的局域网 IPv4。"""
|
||
out: list[tuple[str, str]] = []
|
||
try:
|
||
probe = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
probe.connect(("8.8.8.8", 80))
|
||
addr = probe.getsockname()[0]
|
||
probe.close()
|
||
if addr and not addr.startswith("127."):
|
||
out.append((addr, "默认路由"))
|
||
except OSError:
|
||
pass
|
||
try:
|
||
for info in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET):
|
||
addr = info[4][0]
|
||
if addr and not addr.startswith("127.") and addr not in [a for a, _ in out]:
|
||
out.append((addr, "主机名"))
|
||
except OSError:
|
||
pass
|
||
return out
|
||
|
||
|
||
def wifi_name() -> str:
|
||
text = _run(["netsh", "wlan", "show", "interfaces"])
|
||
for raw in text.splitlines():
|
||
line = raw.strip()
|
||
if line.startswith("SSID") and "BSSID" not in line:
|
||
return line.split(":", 1)[-1].strip()
|
||
return ""
|
||
|
||
|
||
def collect(server, store) -> dict:
|
||
"""把自检需要的东西一次性收集出来。"""
|
||
data: dict = {
|
||
"time": time.strftime("%Y-%m-%d %H:%M:%S"),
|
||
"os": f"{platform.system()} {platform.release()}",
|
||
"ips": local_ips(),
|
||
"wifi": wifi_name(),
|
||
"port": getattr(server, "port", 0),
|
||
"discovery_port": DISCOVERY_PORT,
|
||
"server_running": bool(getattr(server, "running", False)),
|
||
"probes": 0,
|
||
"selfcheck_requests": 0,
|
||
"tcp_connections": 0,
|
||
"connected": [],
|
||
"firewall": {},
|
||
"app_dir": str(app_dir()),
|
||
}
|
||
|
||
# 服务端状态(含自动发现计数、累计 TCP 连接数)
|
||
try:
|
||
status = server.status()
|
||
data["connected"] = status.get("connected", [])
|
||
data["pairing_code"] = status.get("pairing_code", "")
|
||
data["screen_sharing"] = status.get("screen_sharing", False)
|
||
data["tcp_connections"] = int(status.get("total_connections", 0) or 0)
|
||
|
||
info = status.get("discovery") or {}
|
||
data["probes"] = int(info.get("answered", 0) or 0)
|
||
data["selfcheck_requests"] = int(info.get("selfcheck_requests", 0) or 0)
|
||
data["discovery_running"] = bool(info.get("running"))
|
||
data["discovery_error"] = info.get("error", "")
|
||
except Exception as exc: # noqa: BLE001
|
||
data["discovery_running"] = False
|
||
data["discovery_error"] = str(exc)
|
||
|
||
# 防火墙
|
||
try:
|
||
data["firewall"] = firewall_check(data["port"] or 8765, DISCOVERY_PORT)
|
||
except Exception as exc: # noqa: BLE001
|
||
data["firewall"] = {"supported": False, "message": str(exc)}
|
||
|
||
return data
|
||
|
||
|
||
def verdict(data: dict) -> tuple[str, str]:
|
||
"""给出结论:(级别, 文字)。级别是 ok / warn / error。"""
|
||
fw = data.get("firewall") or {}
|
||
if not data.get("server_running"):
|
||
return "error", "被控端没有在运行 —— 先启动它,否则手表一定连不上。"
|
||
|
||
if not fw.get("supported"):
|
||
return "warn", "防火墙状态读不到(可能不是 Windows),无法判断。"
|
||
|
||
if fw.get("blocked_count"):
|
||
return "error", (f"有 {fw['blocked_count']} 条「阻止」规则压着,"
|
||
"手表能搜到但连不上。去「设置/修复」点一键修复。")
|
||
|
||
if not (fw.get("tcp") and fw.get("udp")):
|
||
return "error", "端口放行规则缺失,其他设备连不上。点「一键修复」。"
|
||
|
||
if not fw.get("app_rule"):
|
||
return "warn", ("现在能连上,但程序级放行规则不完整,"
|
||
"以后系统可能再生成阻止规则。建议修一次。")
|
||
|
||
probes = data.get("probes", 0)
|
||
conns = data.get("tcp_connections", 0)
|
||
if probes == 0 and conns == 0:
|
||
return "warn", ("防火墙没问题,但**一个包都没收到**。"
|
||
"手表可能不在同一个 WiFi,或路由器开了 AP 隔离。")
|
||
|
||
return "ok", "一切正常,手表应该能连上。"
|
||
|
||
|
||
def render(data: dict) -> str:
|
||
"""把自检结果渲染成可复制的文本。"""
|
||
level, text = verdict(data)
|
||
lines: list[str] = []
|
||
lines.append("===== 电脑端自检 =====")
|
||
lines.append(f"时间: {data.get('time')}")
|
||
lines.append(f"系统: {data.get('os')}")
|
||
lines.append("")
|
||
|
||
lines.append("--- 本机网络 ---")
|
||
ips = data.get("ips") or []
|
||
if ips:
|
||
for addr, tag in ips:
|
||
lines.append(f" {addr} [{tag}]")
|
||
lines.append(f" >>> 手表上填这个 IP:{ips[0][0]}")
|
||
else:
|
||
lines.append(" 没读到 IP(网络没连上?)")
|
||
if data.get("wifi"):
|
||
lines.append(f" WiFi: {data['wifi']}")
|
||
lines.append("")
|
||
|
||
lines.append("--- 监听 ---")
|
||
lines.append(f" 控制端口 TCP {data.get('port')}: "
|
||
f"{'监听中' if data.get('server_running') else '未监听'}")
|
||
lines.append(f" 发现端口 UDP {data.get('discovery_port')}: "
|
||
f"{'监听中' if data.get('discovery_running') else '未监听'}")
|
||
if data.get("discovery_error"):
|
||
lines.append(f" 错误: {data['discovery_error']}")
|
||
lines.append("")
|
||
|
||
lines.append("--- 收到过多少(关键)---")
|
||
lines.append(f" 自动发现请求: {data.get('probes', 0)} 个")
|
||
lines.append(f" 自检请求: {data.get('selfcheck_requests', 0)} 个")
|
||
lines.append(f" TCP 连接: {data.get('tcp_connections', 0)} 次")
|
||
if data.get("probes", 0) == 0 and data.get("tcp_connections", 0) == 0:
|
||
lines.append(" !! 一直是 0 说明手表的包根本没到这台电脑")
|
||
else:
|
||
lines.append(" 有数字说明手表的包能到达这台电脑")
|
||
lines.append("")
|
||
|
||
lines.append("--- 防火墙 ---")
|
||
fw = data.get("firewall") or {}
|
||
lines.append(f" 端口放行 TCP: {'是' if fw.get('tcp') else '否'}")
|
||
lines.append(f" 端口放行 UDP: {'是' if fw.get('udp') else '否'}")
|
||
lines.append(f" 程序级放行: {'是' if fw.get('app_rule') else '否'} "
|
||
f"({os.path.basename(str(fw.get('app_rule_program') or '')) or '-'})")
|
||
lines.append(f" 阻止规则: {fw.get('blocked_count', 0)} 条(针对本程序)")
|
||
if fw.get("blocked_others"):
|
||
lines.append(f" 另外还有 {fw['blocked_others']} 条是针对别的程序的,不影响")
|
||
lines.append("")
|
||
|
||
lines.append("--- 已连接设备 ---")
|
||
connected = data.get("connected") or []
|
||
if connected:
|
||
for item in connected:
|
||
lines.append(f" {item.get('name', '?')} {item.get('addr', '')}")
|
||
else:
|
||
lines.append(" (没有设备连着)")
|
||
lines.append("")
|
||
|
||
lines.append("--- 结论 ---")
|
||
lines.append(f" [{level.upper()}] {text}")
|
||
lines.append("")
|
||
lines.append("===== 结束 =====")
|
||
return "\n".join(lines)
|