# -*- coding: utf-8 -*- r'''提交这轮修复,顺便看看有没有现成的手表截图能放到官网上。''' from __future__ import annotations import os import subprocess import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") GIT = r"C:\Program Files\Git\bin\git.exe" MSG = """修真凶:4G 链路静默断连导致隧道"假活"(用户撞上 Error 1033) 现象:用户早上打开 relay.xxmxxs.top 看到 Cloudflare Error 1033。 watchdog 日志把过程记下来了: 公网不通(HTTP 000,ha_connections=2) <- 隧道显示连着,公网不通 公网不通(HTTP 502,ha_connections=2) 公网恢复(HTTP 200) <- 自己又好了 也就是说:**间歇性假活**,短样本测不出来。 我之前只测 15~25 次就说"可用率 100%",太草率了。 根因:4G 链路会**静默断开** TCP 连接 —— 不发 FIN/RST,双方都以为还连着。 Cloudflare 把请求发到这条僵尸连接上,就回 502/530/1033。 Linux 默认 tcp_keepalive_time = **7200 秒(2 小时)**才发现连接死了, 所以中间这段时间服务一直"看着在线、实际打不开"。 tunnel.log 最近 3000 行的佐证: i/o timeout 212 次 Retrying connection 127 次 Lost connection 22 次 修法: 1. TCP keepalive 调到 30 秒 + 每 10 秒探测 3 次 => 最多约 60 秒发现僵尸连接,cloudflared 立刻重连 (/etc/sysctl.d/97-ykxmz-keepalive.conf) 2. 隧道连接数 2 -> 4,多几条冗余 3. 修 relay 从不清理设备记录的问题: self.devices 只增不减,被控端每换端口/重装就多一条, 实测一天攒了 75 条僵尸记录,/v1/health 从几 KB 涨到 20KB。 加 DEVICE_TTL(6 小时)在 snapshot() 里清掉。 实测(修完立刻验证): 设备侧 公网 15/15 HTTP 200 Windows 公网 25/25,中位 520 ms,最快 463 ms health 20931 字节 / 75 台 -> 152 字节 / 0 台 """ def commit() -> None: subprocess.run([GIT, "add", "-A"], cwd=str(ROOT), check=False) msg_file = ROOT / "_build" / "_cmsg6.txt" msg_file.write_bytes(MSG.encode("utf-8")) r = subprocess.run([GIT, "commit", "-q", "-F", str(msg_file)], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8", errors="replace") msg_file.unlink(missing_ok=True) print(" commit exit=%s" % r.returncode) show = subprocess.run([GIT, "log", "--oneline", "-1"], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8", errors="replace") print(" " + (show.stdout or "").strip()) env = dict(os.environ) env["GIT_SSH_COMMAND"] = r'"C:\Program Files\Git\usr\bin\ssh.exe"' push = subprocess.run([GIT, "push", "origin", "main"], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8", errors="replace", env=env) for line in (push.stderr or push.stdout or "").strip().splitlines()[-2:]: print(" " + line) def main() -> int: commit() print() print(" 官网现在有什么图可用:") for folder in ("_tests/shots", "android", "windows", "docs", "_build"): base = ROOT / folder if not base.is_dir(): continue pics = [p for p in base.rglob("*") if p.suffix.lower() in (".png", ".jpg", ".jpeg", ".webp") and p.is_file() and p.stat().st_size > 3000] if pics: print(" %s/ (%d 张)" % (folder, len(pics))) for p in sorted(pics)[:8]: print(" %-46s %6.1f KB" % (p.name, p.stat().st_size / 1024)) return 0 if __name__ == "__main__": sys.exit(main())