mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 18:20:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc289c5c21 | ||
|
|
d5a1943d1f | ||
|
|
768309c1f2 | ||
|
|
8dc47d0f48 |
@@ -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
|
## 2.1.1
|
||||||
|
|
||||||
- **修一个会让网站整个挂掉的死锁**(这是「网站还是容易挂」的真因)。
|
- **修一个会让网站整个挂掉的死锁**(这是「网站还是容易挂」的真因)。
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
r'''看当前桌面/窗口的真实状态 —— 诊断"桌面消失、一片蓝"。
|
||||||
|
|
||||||
|
用户报:"我现在桌面消失了,电脑一片蓝,然后还有个蓝色的窗口"。
|
||||||
|
|
||||||
|
已知:
|
||||||
|
18:42:41 机器异常关机(Kernel-Power 41 / EventLog 6008,无蓝屏记录)
|
||||||
|
19:58:23 重新启动
|
||||||
|
20:01:37 explorer 起来
|
||||||
|
自启项里没有 ykxmz,当前也没有 ykxmz 在跑
|
||||||
|
|
||||||
|
要查的是:现在屏幕上到底有什么、是不是有全屏窗口盖住了桌面。
|
||||||
|
'''
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import ctypes.wintypes as wt
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
user32 = ctypes.WinDLL("user32", use_last_error=True)
|
||||||
|
|
||||||
|
EnumWindows = user32.EnumWindows
|
||||||
|
EnumWindows.argtypes = [ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM),
|
||||||
|
wt.LPARAM]
|
||||||
|
|
||||||
|
|
||||||
|
class RECT(ctypes.Structure):
|
||||||
|
_fields_ = [("left", wt.LONG), ("top", wt.LONG),
|
||||||
|
("right", wt.LONG), ("bottom", wt.LONG)]
|
||||||
|
|
||||||
|
|
||||||
|
def windows() -> list:
|
||||||
|
out = []
|
||||||
|
|
||||||
|
def cb(hwnd, _lp):
|
||||||
|
if not user32.IsWindowVisible(hwnd):
|
||||||
|
return True
|
||||||
|
title = ctypes.create_unicode_buffer(512)
|
||||||
|
user32.GetWindowTextW(hwnd, title, 512)
|
||||||
|
cls = ctypes.create_unicode_buffer(256)
|
||||||
|
user32.GetClassNameW(hwnd, cls, 256)
|
||||||
|
r = RECT()
|
||||||
|
user32.GetWindowRect(hwnd, ctypes.byref(r))
|
||||||
|
pid = wt.DWORD()
|
||||||
|
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
|
||||||
|
w, h = r.right - r.left, r.bottom - r.top
|
||||||
|
if w > 80 and h > 80:
|
||||||
|
out.append({"hwnd": hwnd, "title": title.value, "cls": cls.value,
|
||||||
|
"pid": pid.value, "w": w, "h": h,
|
||||||
|
"x": r.left, "y": r.top})
|
||||||
|
return True
|
||||||
|
|
||||||
|
EnumWindows(ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM)(cb), 0)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def proc_name(pid: int) -> str:
|
||||||
|
try:
|
||||||
|
r = subprocess.run(
|
||||||
|
["powershell", "-NoProfile", "-Command",
|
||||||
|
"(Get-Process -Id %d -EA SilentlyContinue).ProcessName" % pid],
|
||||||
|
capture_output=True, text=True, timeout=8)
|
||||||
|
return (r.stdout or "").strip() or "?"
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
return "?"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
sw = user32.GetSystemMetrics(0)
|
||||||
|
sh = user32.GetSystemMetrics(1)
|
||||||
|
print(" 屏幕: %d x %d" % (sw, sh))
|
||||||
|
print(" 当前输入桌面: %s" % _desktop_name())
|
||||||
|
print()
|
||||||
|
|
||||||
|
ws = windows()
|
||||||
|
ws.sort(key=lambda w: w["w"] * w["h"], reverse=True)
|
||||||
|
print(" 可见窗口 %d 个(按面积排序,前 20):" % len(ws))
|
||||||
|
for w in ws[:20]:
|
||||||
|
full = w["w"] >= sw - 4 and w["h"] >= sh - 4
|
||||||
|
pn = proc_name(w["pid"])
|
||||||
|
print(" %s %-14s pid=%-6d %dx%d @(%d,%d)"
|
||||||
|
% ("★全屏" if full else " ", pn, w["pid"],
|
||||||
|
w["w"], w["h"], w["x"], w["y"]))
|
||||||
|
print(" 类=%-22s 标题=%r" % (w["cls"][:22], w["title"][:60]))
|
||||||
|
print()
|
||||||
|
fulls = [w for w in ws if w["w"] >= sw - 4 and w["h"] >= sh - 4]
|
||||||
|
if fulls:
|
||||||
|
print(" ⚠ 有 %d 个全屏窗口 —— 下面这些就是盖住桌面的嫌疑:" % len(fulls))
|
||||||
|
for w in fulls:
|
||||||
|
print(" pid=%d (%s) 类=%s 标题=%r"
|
||||||
|
% (w["pid"], proc_name(w["pid"]), w["cls"], w["title"][:50]))
|
||||||
|
else:
|
||||||
|
print(" ✓ 没有全屏窗口盖着")
|
||||||
|
print()
|
||||||
|
ex = subprocess.run(["powershell", "-NoProfile", "-Command",
|
||||||
|
"Get-Process explorer -EA SilentlyContinue | "
|
||||||
|
"Select-Object -First 1 -ExpandProperty Id"],
|
||||||
|
capture_output=True, text=True, timeout=10)
|
||||||
|
print(" explorer PID: %s" % ((ex.stdout or "").strip() or "**没在跑**"))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _desktop_name() -> str:
|
||||||
|
"""当前输入桌面的名字。Default = 正常,Winlogon = 锁屏。"""
|
||||||
|
h = user32.OpenInputDesktop(0, False, 0x0001)
|
||||||
|
if not h:
|
||||||
|
return "(拿不到 —— 可能锁屏或没有交互桌面)"
|
||||||
|
try:
|
||||||
|
sb = ctypes.create_unicode_buffer(256)
|
||||||
|
need = wt.DWORD()
|
||||||
|
ok = user32.GetUserObjectInformationW(h, 2, sb, 512,
|
||||||
|
ctypes.byref(need))
|
||||||
|
return sb.value if ok else "(拿不到名字)"
|
||||||
|
finally:
|
||||||
|
user32.CloseDesktop(h)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
sys.exit(main())
|
||||||
@@ -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())
|
||||||
@@ -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())
|
||||||
@@ -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())
|
||||||
@@ -17,7 +17,7 @@ import zipfile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
||||||
VERSION = "2.1.1"
|
VERSION = "2.1.2"
|
||||||
NAME = f"ykxmz-{VERSION}"
|
NAME = f"ykxmz-{VERSION}"
|
||||||
|
|
||||||
AGENT_SRC = ROOT / "dist_exe" / "ykxmz"
|
AGENT_SRC = ROOT / "dist_exe" / "ykxmz"
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
r'''稳定性观测:连续打公网,统计可用率。
|
||||||
|
|
||||||
|
用途:验证 2.1.2 的隧道加固(协议 auto + 重试拉大)真的有效。
|
||||||
|
2.1.1 之前是"过一阵就 530",所以只看一次 200 说明不了问题,
|
||||||
|
要**连续观测一段时间**。
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python _build/watch_stability.py # 默认 20 次、间隔 15 秒
|
||||||
|
python _build/watch_stability.py 40 20 # 40 次、间隔 20 秒
|
||||||
|
'''
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
URL = "https://relay.xxmxxs.top/v1/health"
|
||||||
|
|
||||||
|
|
||||||
|
def once(timeout: float = 15.0) -> tuple:
|
||||||
|
"""返回 (状态码文本, 耗时秒)。"""
|
||||||
|
t0 = time.time()
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
URL, headers={"Cache-Control": "no-cache",
|
||||||
|
"User-Agent": "ykxmz-watch"})
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
resp.read(64)
|
||||||
|
return str(resp.status), time.time() - t0
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
try:
|
||||||
|
exc.read(64)
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
pass
|
||||||
|
return str(exc.code), time.time() - t0
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
return "无响应(%s)" % type(exc).__name__, time.time() - t0
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
times = int(sys.argv[1]) if len(sys.argv) > 1 else 20
|
||||||
|
gap = float(sys.argv[2]) if len(sys.argv) > 2 else 15.0
|
||||||
|
|
||||||
|
print("=" * 58)
|
||||||
|
print(" 公网稳定性观测:%d 次,间隔 %.0f 秒(约 %.0f 分钟)"
|
||||||
|
% (times, gap, times * gap / 60))
|
||||||
|
print("=" * 58)
|
||||||
|
|
||||||
|
codes = []
|
||||||
|
durs = []
|
||||||
|
for i in range(1, times + 1):
|
||||||
|
code, dur = once()
|
||||||
|
codes.append(code)
|
||||||
|
durs.append(dur)
|
||||||
|
mark = "✓" if code == "200" else "✗"
|
||||||
|
print(" %s 第 %2d 次: %-8s %.2fs" % (mark, i, code, dur))
|
||||||
|
if i < times:
|
||||||
|
time.sleep(gap)
|
||||||
|
|
||||||
|
ok = sum(1 for c in codes if c == "200")
|
||||||
|
print()
|
||||||
|
print("=" * 58)
|
||||||
|
print(" 200 的次数: %d / %d" % (ok, times))
|
||||||
|
print(" 可用率: %.1f%%" % (100.0 * ok / max(1, times)))
|
||||||
|
if durs:
|
||||||
|
sd = sorted(durs)
|
||||||
|
print(" 延迟 中位: %.2fs P90: %.2fs 最大: %.2fs"
|
||||||
|
% (sd[len(sd) // 2], sd[min(len(sd) - 1, int(len(sd) * 0.9))],
|
||||||
|
sd[-1]))
|
||||||
|
print()
|
||||||
|
if ok == times:
|
||||||
|
print(" ✅ 全程 200 —— 加固有效")
|
||||||
|
elif ok >= times * 0.9:
|
||||||
|
print(" ⚠ 偶尔失败(%d 次)—— 比加固前好,但还没完全稳"
|
||||||
|
% (times - ok))
|
||||||
|
else:
|
||||||
|
print(" ❌ 可用率偏低(%d 次失败)—— 还要继续查"
|
||||||
|
% (times - ok))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -23,7 +23,7 @@ def _latest_apk() -> Path:
|
|||||||
(ROOT / "dist").glob("*watch*.apk"),
|
(ROOT / "dist").glob("*watch*.apk"),
|
||||||
key=lambda p: p.stat().st_mtime, reverse=True)
|
key=lambda p: p.stat().st_mtime, reverse=True)
|
||||||
return candidates[0] if candidates else \
|
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()
|
APK = _latest_apk()
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ ROOT = Path(__file__).resolve().parent.parent
|
|||||||
# 被控端 -> 一个 zip
|
# 被控端 -> 一个 zip
|
||||||
# 服务器端 -> 一个 zip
|
# 服务器端 -> 一个 zip
|
||||||
# 控制端 -> 直接给 APK(不打 zip)
|
# 控制端 -> 直接给 APK(不打 zip)
|
||||||
AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.1.zip"
|
AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.2.zip"
|
||||||
SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.1.zip"
|
SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.2.zip"
|
||||||
WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.1.apk"
|
WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.2.apk"
|
||||||
TOOLCHAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\starklink\toolchain")
|
TOOLCHAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\starklink\toolchain")
|
||||||
JAVAC = TOOLCHAIN / "jdk17" / "bin" / "javac.exe"
|
JAVAC = TOOLCHAIN / "jdk17" / "bin" / "javac.exe"
|
||||||
JAVA = TOOLCHAIN / "jdk17" / "bin" / "java.exe"
|
JAVA = TOOLCHAIN / "jdk17" / "bin" / "java.exe"
|
||||||
@@ -89,7 +89,7 @@ def main() -> int:
|
|||||||
for label, path in (("被控端", agent_dir / "README-agent.txt"),
|
for label, path in (("被控端", agent_dir / "README-agent.txt"),
|
||||||
("服务器端", relay_dir / "README-server.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""
|
raw = path.read_bytes() if path.is_file() else b""
|
||||||
check(f"{label}说明有 UTF-8 BOM",
|
check(f"{label}说明有 UTF-8 BOM",
|
||||||
raw[:3] == b"\xef\xbb\xbf", raw[:3])
|
raw[:3] == b"\xef\xbb\xbf", raw[:3])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.lanremote.watch"
|
package="com.lanremote.watch"
|
||||||
android:versionCode="5"
|
android:versionCode="5"
|
||||||
android:versionName="2.1.1">
|
android:versionName="2.1.2">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ $d8 = Join-Path $bt "d8.bat"
|
|||||||
|
|
||||||
$work = Join-Path $android "build"
|
$work = Join-Path $android "build"
|
||||||
$outDir = Join-Path $root "dist"
|
$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 }
|
function Step($text) { Write-Host "`n=== $text ===" -ForegroundColor Cyan }
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ $unsigned = Join-Path $work "app-unsigned.apk"
|
|||||||
--min-sdk-version 24 `
|
--min-sdk-version 24 `
|
||||||
--target-sdk-version 34 `
|
--target-sdk-version 34 `
|
||||||
--version-code 5 `
|
--version-code 5 `
|
||||||
--version-name "2.1.1" `
|
--version-name "2.1.2" `
|
||||||
--auto-add-overlay
|
--auto-add-overlay
|
||||||
if ($LASTEXITCODE -ne 0) { throw "aapt2 link 失败" }
|
if ($LASTEXITCODE -ne 0) { throw "aapt2 link 失败" }
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
软件名称:ykxmz
|
软件名称:ykxmz
|
||||||
版本:2.1.1
|
版本:2.1.2
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
|
|
||||||
装好之后看这一页:怎么确认它在跑、怎么改设置、
|
装好之后看这一页:怎么确认它在跑、怎么改设置、
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
八、版本记录
|
八、版本记录
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
2.1.1
|
2.1.2
|
||||||
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
|
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
|
||||||
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
|
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
|
||||||
合计 10 条连接),一种协议犯病另一种还在服务
|
合计 10 条连接),一种协议犯病另一种还在服务
|
||||||
@@ -218,6 +218,6 @@
|
|||||||
|
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
软件:ykxmz 2.1.1
|
软件:ykxmz 2.1.2
|
||||||
许可条款见《许可协议》(LICENSE.txt)
|
许可条款见《许可协议》(LICENSE.txt)
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
软件名称:ykxmz
|
软件名称:ykxmz
|
||||||
版本:2.1.1
|
版本:2.1.2
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
|
|
||||||
请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的
|
请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的
|
||||||
@@ -164,6 +164,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
|
|||||||
|
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
软件:ykxmz 2.1.1
|
软件:ykxmz 2.1.2
|
||||||
继续安装,即表示你已阅读并同意《许可协议》。
|
继续安装,即表示你已阅读并同意《许可协议》。
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@
|
|||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
软件名称:ykxmz(远程管理工具)
|
软件名称:ykxmz(远程管理工具)
|
||||||
版本:2.1.1
|
版本:2.1.2
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
生效日期:2026 年 10 月 5 日
|
生效日期: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)。
|
代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
软件名称:ykxmz(远程管理工具)
|
软件名称:ykxmz(远程管理工具)
|
||||||
版本:2.1.1
|
版本:2.1.2
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
生效日期:2026 年 10 月 5 日
|
生效日期: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)。
|
代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。
|
||||||
@@ -173,7 +173,7 @@
|
|||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
软件名称:ykxmz
|
软件名称: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
|
软件名称:ykxmz
|
||||||
版本:2.1.1
|
版本:2.1.2
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
|
|
||||||
装好之后看这一页:怎么确认它在跑、怎么改设置、
|
装好之后看这一页:怎么确认它在跑、怎么改设置、
|
||||||
@@ -539,7 +539,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
|
|||||||
八、版本记录
|
八、版本记录
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
2.1.1
|
2.1.2
|
||||||
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
|
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
|
||||||
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
|
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
|
||||||
合计 10 条连接),一种协议犯病另一种还在服务
|
合计 10 条连接),一种协议犯病另一种还在服务
|
||||||
@@ -560,6 +560,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
|
|||||||
|
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
作者:小麦子
|
作者:小麦子
|
||||||
软件:ykxmz 2.1.1
|
软件:ykxmz 2.1.2
|
||||||
许可条款见《许可协议》(LICENSE.txt)
|
许可条款见《许可协议》(LICENSE.txt)
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@
|
|||||||
; ============================================================================
|
; ============================================================================
|
||||||
|
|
||||||
#define MyAppName "ykxmz"
|
#define MyAppName "ykxmz"
|
||||||
#define MyAppVersion "2.1.1"
|
#define MyAppVersion "2.1.2"
|
||||||
#define MyAppPublisher "小麦子"
|
#define MyAppPublisher "小麦子"
|
||||||
#define MyAppExeName "ykxmz.exe"
|
#define MyAppExeName "ykxmz.exe"
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ Filename: "{app}\{#MyAppExeName}"; Parameters: "--autostart on"; \
|
|||||||
; 代价是安装器和它是父子关系:用"等整个进程树"的方式调安装器会卡住
|
; 代价是安装器和它是父子关系:用"等整个进程树"的方式调安装器会卡住
|
||||||
; (PowerShell 的 Start-Process -Wait 就是)。
|
; (PowerShell 的 Start-Process -Wait 就是)。
|
||||||
; 学校批量部署请用 cmd:
|
; 学校批量部署请用 cmd:
|
||||||
; cmd /c ykxmz-setup-2.1.1.exe /VERYSILENT /CODE=123456
|
; cmd /c ykxmz-setup-2.1.2.exe /VERYSILENT /CODE=123456
|
||||||
; cmd 只等安装器本身,不等孙子进程,不会卡。
|
; cmd 只等安装器本身,不等孙子进程,不会卡。
|
||||||
Filename: "{app}\{#MyAppExeName}"; Parameters: "--silent"; \
|
Filename: "{app}\{#MyAppExeName}"; Parameters: "--silent"; \
|
||||||
Description: "立即启动(后台运行,不显示窗口)"; \
|
Description: "立即启动(后台运行,不显示窗口)"; \
|
||||||
|
|||||||
+31
-5
@@ -204,9 +204,26 @@ fi
|
|||||||
#
|
#
|
||||||
# (早先这里写的是"两条同时跑会互相争抢",那个结论是错的,已作废。
|
# (早先这里写的是"两条同时跑会互相争抢",那个结论是错的,已作废。
|
||||||
# 再早还写过"运营商封 UDP",那个测量方法本身是错的,也作废。)
|
# 再早还写过"运营商封 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}"
|
MAIN_CONNS="${QUIC_CONNS:-8}"
|
||||||
ALT_PROTO=http2
|
ALT_PROTO=auto
|
||||||
ALT_CONNS="${H2_CONNS:-2}"
|
ALT_CONNS="${H2_CONNS:-2}"
|
||||||
|
|
||||||
# metrics 端口必须**显式钉死**。
|
# metrics 端口必须**显式钉死**。
|
||||||
@@ -581,9 +598,18 @@ EOF
|
|||||||
# --edge-ip-version 4 这条线路有 IPv6 地址但实际不通,
|
# --edge-ip-version 4 这条线路有 IPv6 地址但实际不通,
|
||||||
# 边缘默认优先 IPv6 会一直超时
|
# 边缘默认优先 IPv6 会一直超时
|
||||||
# --ha-connections N 每个单元各自多少条连接(默认 quic 8 / h2 2)
|
# --ha-connections N 每个单元各自多少条连接(默认 quic 8 / h2 2)
|
||||||
# --retries 5 连边缘失败时多重试
|
# --retries N 连边缘失败时重试多少次
|
||||||
# --metrics ADDR:PORT **必须显式钉死**,见下面的注释
|
#
|
||||||
TUNNEL_BASE="--no-autoupdate --edge-ip-version 4 --retries 5"
|
# ⚠ --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
|
if [ -n "$TOKEN" ]; then
|
||||||
# Token 模式:Token 存单独的 600 权限文件,不写进 unit
|
# Token 模式:Token 存单独的 600 权限文件,不写进 unit
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ from collections import deque
|
|||||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
VERSION = "2.1.1"
|
VERSION = "2.1.2"
|
||||||
|
|
||||||
# ---------------- 参数(都能用环境变量覆盖)----------------
|
# ---------------- 参数(都能用环境变量覆盖)----------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""局域网遥控 · Windows 被控端。"""
|
"""局域网遥控 · Windows 被控端。"""
|
||||||
|
|
||||||
__version__ = "2.1.1"
|
__version__ = "2.1.2"
|
||||||
APP_NAME = "ykxmz"
|
APP_NAME = "ykxmz"
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ VSVersionInfo(
|
|||||||
[
|
[
|
||||||
StringStruct("CompanyName", "ykxmz"),
|
StringStruct("CompanyName", "ykxmz"),
|
||||||
StringStruct("FileDescription", "ykxmz"),
|
StringStruct("FileDescription", "ykxmz"),
|
||||||
StringStruct("FileVersion", "2.1.1.0"),
|
StringStruct("FileVersion", "2.1.2.0"),
|
||||||
StringStruct("InternalName", "ykxmz"),
|
StringStruct("InternalName", "ykxmz"),
|
||||||
StringStruct("LegalCopyright", "ykxmz"),
|
StringStruct("LegalCopyright", "ykxmz"),
|
||||||
StringStruct("OriginalFilename", "ykxmz.exe"),
|
StringStruct("OriginalFilename", "ykxmz.exe"),
|
||||||
StringStruct("ProductName", "ykxmz"),
|
StringStruct("ProductName", "ykxmz"),
|
||||||
StringStruct("ProductVersion", "2.1.1.0"),
|
StringStruct("ProductVersion", "2.1.2.0"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user