diff --git a/_build/CHANGELOG.md b/_build/CHANGELOG.md index 0e08e21..1f7d9d3 100644 --- a/_build/CHANGELOG.md +++ b/_build/CHANGELOG.md @@ -1,3 +1,35 @@ +## 2.1.2 + +- **隧道稳定性加固**(继续修「网站还是容易挂」)。查到隧道自己的诊断输出: + + ``` + | UDP Connectivity Cloudflare 边缘 FAIL 连不上 | + | TCP Connectivity Cloudflare 边缘 FAIL 连不上或被挡住 | + | Cloudflare API api.cloudflare.com:443 PASS 通的 | + | ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. | + ``` + + 也就是说:**443 通,7844 不通**,而且是**一阵一阵的** + (过几分钟自己又好了,然后再次断)。Cloudflare 隧道的边缘连接走 7844, + 这条线路对它是间歇性阻断 —— 这属于运营商那一侧,设备改不了。 + + **但可以让它自己去适应**: + + - 两条隧道实例原来**各自写死一个协议**(一条只走 QUIC、一条只走 HTTP/2)。 + 那一刻如果 7844 的 UDP 和 TCP 都不通,两条一起死,没有退路。 + 现在都改成 **自动选择**,cloudflared 自己在 QUIC / HTTP/2 之间挑 + 当前能通的那条。等于所有连接都有自适应能力,而不是 + "8 条赌 UDP、2 条赌 TCP"。 + + - 重试次数从 5 提到「一直重试」。原来失败 5 次就退出、靠系统重新拉起, + 每次退出到重启之间网站都是断的;而这条线路的抖动是分钟级的, + 5 次根本扛不过去(实测隧道每 3~10 分钟就被重启一次,公网跟着 530)。 + 现在抖动期间连接自己恢复,整条隧道不退出。 + + **改完实测**:连接数 7+2(接近满配 10),公网连测 5 次全部 200。 + +- 顺手加了日志轮转(隧道日志已经涨到 3 MB,这台小机器硬盘不大)。 + ## 2.1.1 - **修一个会让网站整个挂掉的死锁**(这是「网站还是容易挂」的真因)。 diff --git a/_build/_cmsg_212.txt b/_build/_cmsg_212.txt new file mode 100644 index 0000000..f3bda13 --- /dev/null +++ b/_build/_cmsg_212.txt @@ -0,0 +1,46 @@ +2.1.2:隧道稳定性加固(继续修「网站还是容易挂」) + +2.1.1 修掉了 nettune 死锁之后,公网上到 200,但**过一阵又 530**。 +这次抓到的是隧道自己: + +cloudflared 的 precheck 输出(tunnel.log): + + | UDP Connectivity region1.v2.argotunnel.com FAIL QUIC connection failed | + | TCP Connectivity region1.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable | + | Cloudflare API api.cloudflare.com:443 PASS API is reachable | + | ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. | + +也就是 **443 通、7844 不通**,而且是**间歇性**的(过几分钟自己又好了, +然后再次断)。Cloudflare 隧道的边缘连接走 7844(QUIC 走 UDP 7844、 +HTTP/2 走 TCP 7844)。这条线路对它是间歇性阻断 —— 属于运营商那一侧, +设备改不了,但可以让 cloudflared 自己去适应。 + +== 两个改动 == + +1. **协议从写死改成 auto** + lanremote-tunnel --protocol quic -> --protocol auto + lanremote-tunnel-h2 --protocol http2 -> --protocol auto + 原来两条实例各赌一个协议:那一刻 7844 的 UDP 和 TCP 都不通就一起死, + 没有退路(实测连接数掉到 1/0、公网 530)。改成 auto 之后 + cloudflared 自己在 QUIC / HTTP/2 之间挑当前能通的那条, + 10 条连接全都有自适应能力。 + +2. **--retries 5 -> 1000000** + 原来失败 5 次就退出、靠 systemd Restart=always 拉起来, + 每次退出到重启之间网站都是断的。而这条线路的抖动是分钟级的 —— + 实测隧道每 3~10 分钟就被重启一次(19:37 / 19:40 / 19:43 / 19:50 / 19:52), + 公网跟着一起断。拉大之后抖动期间连接自己恢复,整条隧道不退出。 + +3. 顺手加日志轮转(tunnel.log 已经 2.98 MB,这台机器硬盘不大)。 + +== 实测 == + + 改前:连接数 1/0,公网 530 + 改后:连接数 7+2(接近满配 10),公网连测 5 次全部 200 + +== 同步 == + +relay/deploy.sh 里的 MAIN_PROTO / ALT_PROTO 也改成 auto、 +TUNNEL_BASE 的 --retries 也拉到 1000000,新机器装出来就是加固过的。 + +全量回归 46 个阶段全部通过。 diff --git a/_build/diag_tunnel_down.py b/_build/diag_tunnel_down.py new file mode 100644 index 0000000..d32559c --- /dev/null +++ b/_build/diag_tunnel_down.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +r'''查隧道为什么掉连接(公网 530,连接数 1/0)。 + +上一轮才刚修掉 nettune 死锁、把公网拉到 200;这次重启 nettune 之后 +又变成 530 了。要分清是: + * nettune 又卡了(那个死锁没修干净?) + * 还是隧道自己掉了(cloudflared 的问题) +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from devssh import run # noqa: E402 + +REMOTE = r''' +echo "════ 1. 服务状态 + 有没有卡住的 ════" +for s in lanremote-relay lanremote-tunnel lanremote-tunnel-h2 lanremote-nettune; do + printf ' %-24s %s\n' "$s" "$(systemctl is-active $s)" +done +echo " --- nettune 卡住吗 ---" +systemctl show -p SubState -p MainPID -p ExecMainStartTimestamp lanremote-nettune 2>&1 | sed 's/^/ /' +echo " --- 有没有卡住的 systemctl ---" +ps -eo pid,etime,cmd 2>/dev/null | grep -E 'systemctl|net-tune' | grep -v grep | sed 's/^/ /' + +echo +echo "════ 2. net-tune 的修复在设备上吗 ════" +grep -c NETTUNE_SERVICE_CALL /root/lanremote-relay/net-tune.sh +echo " unit 里有吗: $(grep -c NETTUNE_SERVICE_CALL /etc/systemd/system/lanremote-nettune.service)" + +echo +echo "════ 3. 隧道为什么连接数是 0 ════" +journalctl -u lanremote-tunnel --no-pager -n 25 2>/dev/null | tail -25 | sed 's/^/ /' + +echo +echo "════ 4. 隧道 unit 长什么样(重启策略 / 协议)════" +systemctl cat lanremote-tunnel 2>/dev/null | grep -vE '^#' | head -25 | sed 's/^/ /' + +echo +echo "════ 5. cloudflared 进程 ════" +ps -eo pid,etime,rss,cmd 2>/dev/null | grep cloudflared | grep -v grep | sed 's/^/ /' +''' + + +def main() -> int: + out = run(REMOTE, timeout=300) + text = out[1] if isinstance(out, tuple) else str(out) + print(text) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/diag_tunnel_log.py b/_build/diag_tunnel_log.py new file mode 100644 index 0000000..d5c30f2 --- /dev/null +++ b/_build/diag_tunnel_log.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +r'''看隧道为什么反复掉(公网 530 的真因)。 + +已知: + nettune 死锁已修(SubState=exited,不再卡) + 但隧道每 3~10 分钟被重启一次(部分是我加的预案在救、部分是它自己掉) + 公网 530 + +要查的是 tunnel.log 里 cloudflared 到底在报什么。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from devssh import run # noqa: E402 + +REMOTE = r''' +LOG=/root/lanremote-relay/tunnel.log +H2LOG=/root/lanremote-relay/tunnel-h2.log + +echo "════ 1. tunnel.log 里的错误行(最近 40 条)════" +grep -iE 'error|fail|warn|timeout|retry|refused|EOF|unregister|connection' "$LOG" 2>/dev/null | tail -40 | sed 's/^/ /' + +echo +echo "════ 2. tunnel.log 最后 25 行(原样)════" +tail -25 "$LOG" 2>/dev/null | sed 's/^/ /' + +echo +echo "════ 3. h2 那份的日志 ════" +tail -15 "$H2LOG" 2>/dev/null | sed 's/^/ /' + +echo +echo "════ 4. 两份日志多大(写爆了会拖慢小机器)════" +ls -la "$LOG" "$H2LOG" 2>/dev/null | sed 's/^/ /' + +echo +echo "════ 5. cloudflared 版本 + 配置 ════" +/usr/local/bin/cloudflared --version 2>&1 | sed 's/^/ /' +echo " --- config.yml ---" +cat /root/.cloudflared/config.yml 2>/dev/null | sed 's/^/ /' + +echo +echo "════ 6. 现在两条隧道的连接数 ════" +for p in 20241 20242; do + n=$(curl -s --max-time 5 "http://127.0.0.1:$p/metrics" 2>/dev/null | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' | head -1) + echo " $p -> ${n:-拿不到}" +done + +echo +echo "════ 7. 公网现在通吗 ════" +echo " $(curl -s -o /dev/null -w '%{http_code}' --max-time 15 https://relay.xxmxxs.top/v1/health)" +''' + + +def main() -> int: + out = run(REMOTE, timeout=300) + text = out[1] if isinstance(out, tuple) else str(out) + print(text) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/harden_tunnel.py b/_build/harden_tunnel.py new file mode 100644 index 0000000..a835143 --- /dev/null +++ b/_build/harden_tunnel.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +r'''增强隧道稳定性 —— 「网站还是容易挂」的真因修法。 + +== 查到什么 == + +cloudflared 自己的 precheck 输出(tunnel.log): + + | UDP Connectivity region1.v2.argotunnel.com FAIL QUIC connection failed | + | UDP Connectivity region2.v2.argotunnel.com FAIL QUIC connection failed | + | TCP Connectivity region1.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable | + | TCP Connectivity region2.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable | + | Cloudflare API api.cloudflare.com:443 PASS API is reachable | + | ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. | + + 也就是:**443 通,7844 不通**,而且是**间歇性**的 + (日志后面又出现 Registered tunnel connection,过一阵又掉)。 + + Cloudflare 隧道的边缘连接走 7844(QUIC 走 UDP 7844、HTTP/2 走 TCP 7844)。 + 这条线路对 7844 是间歇性阻断的 —— 这不是设备能修的, + 但**可以让 cloudflared 自己去适应**。 + +== 现在的配置有两个问题 == + + 1. **两条实例各写死一个协议** + lanremote-tunnel --protocol quic (只走 UDP 7844) + lanremote-tunnel-h2 --protocol http2 (只走 TCP 7844) + 如果那一刻 7844 的 UDP 和 TCP 都不通,两条一起死 —— 没有退路。 + 实测就是这样:连接数掉到 1/0,公网 530。 + + 2. **--retries 5** + cloudflared 重试 5 次就**退出**,靠 systemd Restart=always 拉起来。 + 每次退出/重启期间网站都是断的。而这个线路的抖动是分钟级的, + 5 次远远不够扛过去。 + +== 改法 == + + 1. `--protocol auto` —— 让 cloudflared **自己**在 QUIC 和 HTTP/2 之间选, + 哪条通走哪条。两条实例都改成 auto,等于 10 条连接全都能自适应。 + (而不是现在这样"8 条赌 UDP、2 条赌 TCP") + + 2. `--retries` 提到很大(相当于"一直重试,别退出")—— + 抖动期间连接会自己恢复,不会整条隧道退出让网站断掉。 + + 3. 顺手加日志轮转:tunnel.log 已经涨到 2.98 MB, + 这台机器只有 460MB 内存 + 小硬盘,无限增长不合适。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from devssh import run # noqa: E402 + + +def patch_unit(name: str, proto: str) -> str: + """读设备上现有的 unit,改协议和重试次数。 + + 用 sed 现场改而不是整份重写 —— 现有 unit 里还有 + metrics 端口、ha-connections、MemoryMax 这些实测调出来的参数, + 整份重写容易漏掉。 + """ + cmds = [ + # 协议 + "sed -i 's/--protocol [a-z0-9]*/--protocol %s/' %s" % (proto, name), + # 重试次数:改成很大(一直重试) + "sed -i 's/--retries [0-9]*/--retries 1000000/' %s" % name, + ] + return " && ".join(cmds) + + +def main() -> int: + print("=" * 62) + print(" 隧道稳定性:协议改 auto + 加大重试 + 日志轮转") + print("=" * 62) + + UNIT_DIR = "/etc/systemd/system" + + print() + print(" ── 1. 两条隧道都改成 --protocol auto ──") + out = run( + "cd %s && " + "sed -i 's/--protocol [a-z0-9]*/--protocol auto/' " + "lanremote-tunnel.service lanremote-tunnel-h2.service && " + "echo ' tunnel:'; grep -o -- '--protocol [a-z0-9]*' " + "lanremote-tunnel.service; " + "echo ' h2:'; grep -o -- '--protocol [a-z0-9]*' " + "lanremote-tunnel-h2.service; " + # 重试次数 + "sed -i 's/--retries [0-9]*/--retries 1000000/' " + "lanremote-tunnel.service lanremote-tunnel-h2.service && " + "echo ' 重试:'; grep -o -- '--retries [0-9]*' " + "lanremote-tunnel.service lanremote-tunnel-h2.service" + % UNIT_DIR, timeout=120) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" ── 2. 日志轮转(tunnel.log 已经 2.98 MB)──") + out = run( + "cat > /etc/logrotate.d/lanremote <<'EOF'\n" + "/root/lanremote-relay/*.log {\n" + " size 5M\n" + " rotate 2\n" + " compress\n" + " missingok\n" + " notifempty\n" + " copytruncate\n" + "}\n" + "EOF\n" + "echo ' logrotate 配置已写'; " + "logrotate --debug /etc/logrotate.d/lanremote 2>&1 | tail -3", + timeout=120) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" ── 3. 重启两条隧道让新参数生效 ──") + out = run( + "systemctl daemon-reload && " + "systemctl restart lanremote-tunnel lanremote-tunnel-h2 && " + "echo ' 等 40 秒让连接建立…' && sleep 40 && " + "echo \" tunnel: $(systemctl is-active lanremote-tunnel)\"; " + "echo \" h2: $(systemctl is-active lanremote-tunnel-h2)\"", + timeout=300) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" ── 4. 连接数 ──") + out = run( + "for p in 20241 20242; do " + "n=$(curl -s --max-time 5 http://127.0.0.1:$p/metrics 2>/dev/null " + "| grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' " + "| head -1); " + "echo \" $p -> ${n:-拿不到}\"; done; " + "echo ' --- 用的什么协议 ---'; " + "grep -o 'protocol=[a-z0-9]*' /root/lanremote-relay/tunnel.log 2>/dev/null " + "| tail -6 | sort | uniq -c | sed 's/^/ /'", timeout=120) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" ── 5. 公网(连测 5 次看稳不稳)──") + out = run( + "for i in 1 2 3 4 5; do " + "c=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 " + "https://relay.xxmxxs.top/v1/health); " + "echo \" 第 $i 次: $c\"; sleep 3; done", timeout=200) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" 说明:3844/7844 那条线路封不封是运营商的事,设备改不了。") + print(" 但 --protocol auto 让 cloudflared 自己绕,") + print(" --retries 拉大让它别退出 —— 抖动期间能自己恢复。") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/package_remote.py b/_build/package_remote.py index df84ea4..9dbff12 100644 --- a/_build/package_remote.py +++ b/_build/package_remote.py @@ -17,7 +17,7 @@ import zipfile from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") -VERSION = "2.1.1" +VERSION = "2.1.2" NAME = f"ykxmz-{VERSION}" AGENT_SRC = ROOT / "dist_exe" / "ykxmz" diff --git a/_tests/test_apk_contents.py b/_tests/test_apk_contents.py index 817a5a3..a2ec396 100644 --- a/_tests/test_apk_contents.py +++ b/_tests/test_apk_contents.py @@ -23,7 +23,7 @@ def _latest_apk() -> Path: (ROOT / "dist").glob("*watch*.apk"), key=lambda p: p.stat().st_mtime, reverse=True) return candidates[0] if candidates else \ - ROOT / "dist" / "ykxmz-watch-2.1.1.apk" + ROOT / "dist" / "ykxmz-watch-2.1.2.apk" APK = _latest_apk() diff --git a/_tests/test_package_remote.py b/_tests/test_package_remote.py index 67534ba..57f6eda 100644 --- a/_tests/test_package_remote.py +++ b/_tests/test_package_remote.py @@ -26,9 +26,9 @@ ROOT = Path(__file__).resolve().parent.parent # 被控端 -> 一个 zip # 服务器端 -> 一个 zip # 控制端 -> 直接给 APK(不打 zip) -AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.1.zip" -SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.1.zip" -WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.1.apk" +AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.2.zip" +SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.2.zip" +WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.2.apk" TOOLCHAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\starklink\toolchain") JAVAC = TOOLCHAIN / "jdk17" / "bin" / "javac.exe" JAVA = TOOLCHAIN / "jdk17" / "bin" / "java.exe" @@ -89,7 +89,7 @@ def main() -> int: for label, path in (("被控端", agent_dir / "README-agent.txt"), ("服务器端", relay_dir / "README-server.txt"), ("控制端(旁边那份)", - ROOT / "dist" / "ykxmz-watch-2.1.1-说明.txt")): + ROOT / "dist" / "ykxmz-watch-2.1.2-说明.txt")): raw = path.read_bytes() if path.is_file() else b"" check(f"{label}说明有 UTF-8 BOM", raw[:3] == b"\xef\xbb\xbf", raw[:3]) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 43c9b04..3e2e3d1 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="2.1.2"> diff --git a/android/build_apk.ps1 b/android/build_apk.ps1 index 057d8b2..047e861 100644 --- a/android/build_apk.ps1 +++ b/android/build_apk.ps1 @@ -27,7 +27,7 @@ $d8 = Join-Path $bt "d8.bat" $work = Join-Path $android "build" $outDir = Join-Path $root "dist" -$apkName = "ykxmz-watch-2.1.1.apk" +$apkName = "ykxmz-watch-2.1.2.apk" function Step($text) { Write-Host "`n=== $text ===" -ForegroundColor Cyan } @@ -65,7 +65,7 @@ $unsigned = Join-Path $work "app-unsigned.apk" --min-sdk-version 24 ` --target-sdk-version 34 ` --version-code 5 ` - --version-name "2.1.1" ` + --version-name "2.1.2" ` --auto-add-overlay if ($LASTEXITCODE -ne 0) { throw "aapt2 link 失败" } diff --git a/docs/AFTER-INSTALL.txt b/docs/AFTER-INSTALL.txt index 1c07126..4d6daeb 100644 --- a/docs/AFTER-INSTALL.txt +++ b/docs/AFTER-INSTALL.txt @@ -3,7 +3,7 @@ ======================================================== 软件名称:ykxmz -版本:2.1.1 +版本:2.1.2 作者:小麦子 装好之后看这一页:怎么确认它在跑、怎么改设置、 @@ -197,7 +197,7 @@ 八、版本记录 -------------------------------------------------------- - 2.1.1 + 2.1.2 * 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍 * 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2, 合计 10 条连接),一种协议犯病另一种还在服务 @@ -218,6 +218,6 @@ -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 许可条款见《许可协议》(LICENSE.txt) -------------------------------------------------------- diff --git a/docs/BEFORE-INSTALL.txt b/docs/BEFORE-INSTALL.txt index 705872b..a76a9a5 100644 --- a/docs/BEFORE-INSTALL.txt +++ b/docs/BEFORE-INSTALL.txt @@ -3,7 +3,7 @@ ======================================================== 软件名称:ykxmz -版本:2.1.1 +版本:2.1.2 作者:小麦子 请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的 @@ -164,6 +164,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成: -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 继续安装,即表示你已阅读并同意《许可协议》。 -------------------------------------------------------- diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt index f4373c8..d5249ae 100644 --- a/docs/LICENSE.txt +++ b/docs/LICENSE.txt @@ -3,7 +3,7 @@ ======================================================== 软件名称:ykxmz(远程管理工具) -版本:2.1.1 +版本:2.1.2 作者:小麦子 生效日期:2026 年 10 月 5 日 @@ -16,7 +16,7 @@ > 都不许拿去做下面第三条那些事。** > > (早先这版文件写的是"软件许可协议",里面有一条"不得修改"—— -> 那和 AGPL 冲突,所以 2.1.1 起拆开了:代码授权归 LICENSE, +> 那和 AGPL 冲突,所以 2.1.2 起拆开了:代码授权归 LICENSE, > 使用约束归这份文件。) @@ -160,7 +160,7 @@ -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 如果你不同意本条款的任何一条,请不要安装或使用本软件。 代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。 diff --git a/installer/license-combined.txt b/installer/license-combined.txt index 34fbe69..b83e78d 100644 --- a/installer/license-combined.txt +++ b/installer/license-combined.txt @@ -3,7 +3,7 @@ ======================================================== 软件名称:ykxmz(远程管理工具) -版本:2.1.1 +版本:2.1.2 作者:小麦子 生效日期:2026 年 10 月 5 日 @@ -16,7 +16,7 @@ > 都不许拿去做下面第三条那些事。** > > (早先这版文件写的是"软件许可协议",里面有一条"不得修改"—— -> 那和 AGPL 冲突,所以 2.1.1 起拆开了:代码授权归 LICENSE, +> 那和 AGPL 冲突,所以 2.1.2 起拆开了:代码授权归 LICENSE, > 使用约束归这份文件。) @@ -160,7 +160,7 @@ -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 如果你不同意本条款的任何一条,请不要安装或使用本软件。 代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。 @@ -173,7 +173,7 @@ ======================================================== 软件名称:ykxmz -版本:2.1.1 +版本:2.1.2 作者:小麦子 请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的 @@ -334,7 +334,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成: -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 继续安装,即表示你已阅读并同意《许可协议》。 -------------------------------------------------------- @@ -345,7 +345,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成: ======================================================== 软件名称:ykxmz -版本:2.1.1 +版本:2.1.2 作者:小麦子 装好之后看这一页:怎么确认它在跑、怎么改设置、 @@ -539,7 +539,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成: 八、版本记录 -------------------------------------------------------- - 2.1.1 + 2.1.2 * 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍 * 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2, 合计 10 条连接),一种协议犯病另一种还在服务 @@ -560,6 +560,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成: -------------------------------------------------------- 作者:小麦子 - 软件:ykxmz 2.1.1 + 软件:ykxmz 2.1.2 许可条款见《许可协议》(LICENSE.txt) -------------------------------------------------------- diff --git a/installer/ykxmz.iss b/installer/ykxmz.iss index cededa2..c52e3de 100644 --- a/installer/ykxmz.iss +++ b/installer/ykxmz.iss @@ -27,7 +27,7 @@ ; ============================================================================ #define MyAppName "ykxmz" -#define MyAppVersion "2.1.1" +#define MyAppVersion "2.1.2" #define MyAppPublisher "小麦子" #define MyAppExeName "ykxmz.exe" @@ -163,7 +163,7 @@ Filename: "{app}\{#MyAppExeName}"; Parameters: "--autostart on"; \ ; 代价是安装器和它是父子关系:用"等整个进程树"的方式调安装器会卡住 ; (PowerShell 的 Start-Process -Wait 就是)。 ; 学校批量部署请用 cmd: -; cmd /c ykxmz-setup-2.1.1.exe /VERYSILENT /CODE=123456 +; cmd /c ykxmz-setup-2.1.2.exe /VERYSILENT /CODE=123456 ; cmd 只等安装器本身,不等孙子进程,不会卡。 Filename: "{app}\{#MyAppExeName}"; Parameters: "--silent"; \ Description: "立即启动(后台运行,不显示窗口)"; \ diff --git a/relay/deploy.sh b/relay/deploy.sh index 66c0081..251df13 100644 --- a/relay/deploy.sh +++ b/relay/deploy.sh @@ -204,9 +204,26 @@ fi # # (早先这里写的是"两条同时跑会互相争抢",那个结论是错的,已作废。 # 再早还写过"运营商封 UDP",那个测量方法本身是错的,也作废。) -MAIN_PROTO=quic +# ⚠ 用 auto,不要写死 quic / http2。 +# +# 实测(2026-10-08)这条线路对 Cloudflare 隧道的 **7844 端口** +# (QUIC 走 UDP 7844、HTTP/2 走 TCP 7844)是**间歇性阻断**的, +# 而 443 一直是通的。cloudflared 自己的 precheck 报得很清楚: +# +# | UDP Connectivity region1.v2.argotunnel.com FAIL QUIC connection failed | +# | TCP Connectivity region1.v2.argotunnel.com FAIL HTTP/2 connection is blocked or unreachable | +# | Cloudflare API api.cloudflare.com:443 PASS API is reachable | +# | ERROR: Allow outbound QUIC traffic on port 7844 or use HTTP2. | +# +# 写死协议的时候,那一刻 7844 的 UDP 和 TCP 都不通 -> 两条实例一起死 +# -> 连接数掉到 1/0 -> 公网 530。实测就是这么挂的。 +# +# 改成 auto 之后 **cloudflared 自己**在 QUIC / HTTP/2 之间挑 +# 当前能通的那条,两条实例都带自适应能力(而不是 +# "8 条赌 UDP、2 条赌 TCP")。改完实测连接数 7+2、公网连测 5 次全 200。 +MAIN_PROTO=auto MAIN_CONNS="${QUIC_CONNS:-8}" -ALT_PROTO=http2 +ALT_PROTO=auto ALT_CONNS="${H2_CONNS:-2}" # metrics 端口必须**显式钉死**。 @@ -581,9 +598,18 @@ EOF # --edge-ip-version 4 这条线路有 IPv6 地址但实际不通, # 边缘默认优先 IPv6 会一直超时 # --ha-connections N 每个单元各自多少条连接(默认 quic 8 / h2 2) -# --retries 5 连边缘失败时多重试 -# --metrics ADDR:PORT **必须显式钉死**,见下面的注释 -TUNNEL_BASE="--no-autoupdate --edge-ip-version 4 --retries 5" +# --retries N 连边缘失败时重试多少次 +# +# ⚠ --retries 从 5 提到 1000000(相当于"一直重试,别退出")。 +# +# 原来 5 次就退出,靠 systemd Restart=always 拉起来 —— +# 每次退出到重启之间网站都是断的。而这条线路对 7844 的阻断是 +# **分钟级**的,5 次重试远远扛不过去:实测隧道每 3~10 分钟 +# 就被重启一次,公网跟着 530。 +# +# 拉大之后,抖动期间连接自己恢复,整条隧道不退出, +# 网站也就不会跟着断。 +TUNNEL_BASE="--no-autoupdate --edge-ip-version 4 --retries 1000000" if [ -n "$TOKEN" ]; then # Token 模式:Token 存单独的 600 权限文件,不写进 unit diff --git a/relay/relay.py b/relay/relay.py index d685121..6319137 100644 --- a/relay/relay.py +++ b/relay/relay.py @@ -59,7 +59,7 @@ from collections import deque from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path -VERSION = "2.1.1" +VERSION = "2.1.2" # ---------------- 参数(都能用环境变量覆盖)---------------- diff --git a/windows/lanremote/__init__.py b/windows/lanremote/__init__.py index 9d674e2..dc96ef1 100644 --- a/windows/lanremote/__init__.py +++ b/windows/lanremote/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- """局域网遥控 · Windows 被控端。""" -__version__ = "2.1.1" +__version__ = "2.1.2" APP_NAME = "ykxmz" diff --git a/windows/version_info.txt b/windows/version_info.txt index 120d91b..290b0fd 100644 --- a/windows/version_info.txt +++ b/windows/version_info.txt @@ -27,12 +27,12 @@ VSVersionInfo( [ StringStruct("CompanyName", "ykxmz"), StringStruct("FileDescription", "ykxmz"), - StringStruct("FileVersion", "2.1.1.0"), + StringStruct("FileVersion", "2.1.2.0"), StringStruct("InternalName", "ykxmz"), StringStruct("LegalCopyright", "ykxmz"), StringStruct("OriginalFilename", "ykxmz.exe"), StringStruct("ProductName", "ykxmz"), - StringStruct("ProductVersion", "2.1.1.0"), + StringStruct("ProductVersion", "2.1.2.0"), ], ) ]