# -*- coding: utf-8 -*- r'''纠正一个我自己传出去的**错误结论**。 == 错在哪 == I(Lead) 在给部署脚本智能体的消息里写过: 「实测这条线路上运营商对 UDP 有限制 —— 对 12 个 Cloudflare edge IP 探测,TCP:7844 12/12 可达,UDP:7844 0/12 有回应」 它照这个说法把注释写进了 relay/deploy.sh。 **但这个测量是无效的。** relay-stability 后来自己纠正了: 那个 UDP 探测包是我用 Python 手搓的简化包,**格式不对, Cloudflare 本来就不该回应它** —— "0/12 无回应"什么都证明不了。 用 `cloudflared --loglevel debug` 跑临时实例,真相是: INF | UDP Connectivity region1.v2.argotunnel.com PASS QUIC connection successful INF | UDP Connectivity region2.v2.argotunnel.com PASS QUIC connection successful INF | SUMMARY: Environment is healthy. cloudflared will use 'quic' as primary protocol INF Registered tunnel connection (握手 1 秒) **UDP 完全通,握手成功时只要 1 秒。** == 真因 == QUIC 握手需要 6+ 个包的往返; TCP 的 SYN 只有 3 个包,而且内核会按 1s/2s/4s 激进重传。 在约 5.4%/包 的丢包下: 成功率 ≈ (1 - p)^6 = (1 - 0.054)^6 ≈ 72% <- 和实测的 72% 对得上 所以 QUIC 的弱点是「**连不上**」,不是「连上之后不好」; 一旦连上,它没有 TCP 的跨流队头阻塞,反而更稳。 == 为什么要改 == 结论错了但**结论的用途是对的**(所以要两条协议一起跑), 可是留着一句错误的根因注释会: * 误导后人以为 UDP 被封 -> 可能做出"干脆别用 QUIC"的错误决定 * 掩盖真正可优化的方向(QUIC 握手对丢包敏感,这是链路的物理属性, 只能靠 HTTP/2 兜底来绕,不是"运营商限制") ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") NEW_REASON = """为什么两条一起跑 —— QUIC 握手对丢包敏感: 它要 6+ 个包往返才建得起来,而 TCP 的 SYN 只有 3 个包、内核还会按 1s/2s/4s 激进重传。 在约 5% 丢包下 (1-p)^6 只有 ~72% 能建成 —— 所以刻意让 HTTP/2 占几条连接兜住那 28%。 (UDP 本身是通的:cloudflared 自检 PASS, 握手成功时只要 1 秒。不是"运营商封了 UDP"。)""" NEW_BLOCK = """# 根因也查清了:**QUIC 握手对丢包敏感**。 # # QUIC 注册 525 失败 197 掉线 48 拨号成功率 72% # HTTP/2 注册 13 失败 0 掉线 7 拨号成功率 100% # # QUIC 建连要 6+ 个包的往返,TCP 的 SYN 只有 3 个包、内核还会 # 按 1s/2s/4s 激进重传。约 5.4%/包的丢包下: # # (1 - 0.054)^6 ≈ 72% <- 和实测的 72% 对得上 # # 注意:**这不是"运营商封了 UDP"**。曾经这么以为过,但那个判断 # 来自一个格式不对的手搓 UDP 探测包(Cloudflare 本来就不该回应它)。 # 用 cloudflared --loglevel debug 实测: # # INF | UDP Connectivity region1.v2.argotunnel.com PASS # INF | UDP Connectivity region2.v2.argotunnel.com PASS # INF | SUMMARY: Environment is healthy ... # INF Registered tunnel connection (握手 1 秒) # # UDP 是通的。QUIC 的弱点在"连不上",不在"连上之后不好"—— # 一旦连上,它没有 TCP 的跨流队头阻塞,反而更稳。 # 所以对策是「多 QUIC + 少量 HTTP/2 兜底」,而不是放弃 QUIC。""" def patch(path: Path, old: str, new: str, label: str) -> bool: if not path.is_file(): print(" [跳过] %s 不存在" % path.name) return True text = path.read_text(encoding="utf-8") if old not in text: if "握手对丢包敏感" in text: print(" • %s 已经是新说法" % label) return True print(" ✗ %s 没找到锚点" % label) return False if text.count(old) != 1: print(" ! %s 锚点出现 %d 次" % (label, text.count(old))) return False path.write_text(text.replace(old, new, 1), encoding="utf-8", newline="\n") print(" ✓ %s" % label) return True def main() -> int: ok = True ok &= patch( ROOT / "relay" / "deploy.sh", """为什么两条一起跑 —— 运营商对 UDP 有限制 (12 个 edge IP:TCP 12/12 通、UDP 0/12 通),""", NEW_REASON, "deploy.sh --help 里的说法") ok &= patch( ROOT / "relay" / "deploy.sh", """# 根因也查清了:**运营商对 UDP 有限制**。 # # 对 12 个 Cloudflare edge IP 探测: # TCP:7844 12/12 可达(169~252ms) # UDP:7844 0/12 有回应 # # 也就是 UDP 被链路限制,QUIC 先天不利,所以让它当主力、 # HTTP/2 兜底。""", NEW_BLOCK, "deploy.sh 的根因注释") ok &= patch( ROOT / "_build" / "test_deploy_sh.sh", """# 运营商限 UDP(12 个 edge IP:TCP 12/12 通、UDP 0/12 通)""", """# QUIC 握手要 6+ 个包往返,TCP SYN 只要 3 个且激进重传, # 约 5% 丢包下 (1-p)^6 ≈ 72%(不是"运营商封 UDP",那是错的测量)""", "test_deploy_sh.sh 的说明") print() if not ok: print(" 有锚点没匹配") return 1 print(" 纠正完成") return 0 if __name__ == "__main__": sys.exit(main())