mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 17:10:43 +00:00
我在给部署脚本的指示里写过:
实测这条线路上运营商对 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),
要么别下结论。
142 lines
5.5 KiB
Python
142 lines
5.5 KiB
Python
# -*- 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())
|