Files
ykxmz/_build/fix_udp_claim.py
xmz7891 e611b07fb1 纠正一个传出去的错误结论:不是「运营商封了 UDP」
我在给部署脚本的指示里写过:

    实测这条线路上运营商对 UDP 有限制 ——
    12 个 Cloudflare edge IP:TCP 12/12 通、UDP 0/12 通

它照这个把注释写进了 relay/deploy.sh。

**但这个测量是无效的。** relay-stability 后来自己纠正了:
那个 UDP 探测包是手搓的简化包,格式不对,
Cloudflare 本来就不该回应它 —— 「0/12 无回应」什么都证明不了。

用 cloudflared --loglevel debug 跑临时实例,真相是:

    UDP Connectivity  region1/region2  PASS  QUIC connection successful
    SUMMARY: Environment is healthy. cloudflared will use 'quic'
    Registered tunnel connection        (握手 1 秒)

**UDP 完全通。**

真因是:QUIC 建连要 6+ 个包往返,而 TCP 的 SYN 只有 3 个包、
内核还会按 1s/2s/4s 激进重传。约 5.4%/包 丢包下:

    (1 - 0.054)^6 ≈ 72%     <- 和实测的 72% 对得上

所以 QUIC 的弱点在「连不上」,不在「连上之后不好」;
一旦连上,它没有 TCP 的跨流队头阻塞,反而更稳。

结论的**用途**没变(还是要两条协议一起跑),但留着错误的根因注释会:
  * 误导后人以为 UDP 被封 -> 可能做出「干脆别用 QUIC」的错误决定
  * 掩盖真正可优化的方向(QUIC 握手对丢包敏感是链路物理属性,
    只能靠 HTTP/2 兜底来绕,不是「运营商限制」)

改动:relay/deploy.sh 两处、_build/test_deploy_sh.sh 一处。

教训记一笔:**手搓协议包去探测可达性非常容易得出错误结论** ——
那个包格式不对,对方根本没义务回应。要么用官方工具的自检
(cloudflared 自己会打 UDP Connectivity PASS/FAIL),
要么别下结论。
2026-10-07 18:22:19 +08:00

142 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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())