发布于 更新于 约 9 分钟读完 · 3,271 字

本文目录 17 节
  1. 事实记录(不可修改区)
  2. 症状与首轮诊断
  3. 先排除的两个常见怀疑(都不成立)
  4. 节点评测
  5. 修复前后
  6. 三个把我带偏的工具故障
  7. 落地
  8. 一句话总结
  9. 五条可复用 insight
  10. 1. fake-ip 模式下的 ping 是自问自答 ⚠️首次
  11. 2. 延迟不等于带宽,内置延迟测试选不出能看视频的节点 ⚠️首次
  12. 3. 探针返回「全 0」时,先怀疑探针 ⭐ 跨项目二次验证
  13. 4. 运行态改了 ≠ 配置落盘 ⚠️首次
  14. 5. 非 ASCII 标识符遇上编码不匹配 = 静默走错分支 ⚠️首次
  15. 顺手教训
  16. 下次改进
  17. 关联文档

ping 通不等于路通 · fake-ip 假信号与节点带宽实测选型

入档:2026-07-27 来源:一次「能 ping 通 coursera.org,但浏览器看课程极卡」的排障会话;本机 Windows 10 Pro + Clash Verge Rev(verge-mihomo v1.19.21,rule 模式,TUN 关闭,系统代理 127.0.0.1:7897) 状态:已定位并修复,运行态 + 配置文件双写落地;insight 3 是 全站文字截断体检_检测工具本身要先被证伪_v1 的跨项目二次验证(那篇标注「尚未跨项目二次验证」,本次补上)

事实记录(不可修改区)

症状与首轮诊断

  • 用户主诉:ping coursera.org 通,浏览器看课程极卡。

  • DNS 实测:www.coursera.org → 198.18.0.4,d3njjcbhbojbot.cloudfront.net(视频 CDN)→ 198.18.0.6,另三个域名同段。dns_config.yaml 确认 enhanced-mode: fake-ip、fake-ip-range: 198.18.0.1/16。

  • 原节点「🇯🇵 VIP1 日本01 - 三网直连【倍率2.0】」实测:

    目标TLS 握手首字节吞吐
    www.coursera.org6.36s6.81s60 KB/s(785KB 用了 13.0s)
    api.coursera.org6.80s7.34s—
    视频 CDN1.70s2.38s—
    持续下行(20s 采样)——305 KB/s ≈ 2.4 Mbps

先排除的两个常见怀疑(都不成立)

  • 不是流量超额限速:profiles.yaml 的 extra 记录 total 1.44 TB、已用 upload 547 MB + download 3.38 GB(约 3.9 GB),expire 换算为 2026-10-25。
  • 不是规则配错:/rules 共 10540 条,DomainSuffix cloudfront.net → 国外媒体,而「国外媒体」组 now = 国外默认(跟随);coursera.org 无专用规则,走末尾 MATCH → 国外默认。两条路径都汇到同一个组,改组即全覆盖,无需新增规则。

节点评测

40 个节点,内置延迟测试 34 个可用。对延迟前 14 名逐个切换 + 实测带宽(speed.cloudflare.com,50 MB 上限、15 秒窗口):

排名带宽延迟节点
1133.33 Mbps72ms🇯🇵 日本03【1.0】
2133.33 Mbps92ms🇸🇬 新加坡01【1.0】
3100 Mbps36ms🇭🇰 香港03【1.0】
450 Mbps126ms🇮🇳 印度01【1.0】
540 Mbps123ms🇰🇷 韩国01【1.0】
…
135.07 Mbps91ms🇯🇵 日本10【0.5】
142.72 Mbps113ms🇯🇵 日本01【2.0】← 原用节点

对前 3 名用 Coursera 真实链路复测,香港03 全面最优(coursera.org TLS 0.203s、视频 CDN TLS 0.203s / TTFB 0.282s、api 0.843s)。定香港03。

修复前后

日本01(原)香港03(现)
持续带宽2.72 Mbps100–133 Mbps
coursera.org TLS6.36s0.19–0.25s
视频 CDN TTFB2.38s0.27s
流量倍率2.01.0

Coursera 720p 约需 3 Mbps —— 原节点 2.72 Mbps 连 480p 都紧张,且播放器每取一个分片都要重新握手,每次干等 6 秒。

三个把我带偏的工具故障

  1. 测速全 0:首轮 14 个节点全部返回 0 Mbps,形似「所有节点都挂了」。实为探针请求 bytes=209715200 超出 Cloudflare 单请求上限,返回 403 + size=1。改回 50 MB 后立刻测出 17.5 MB/s。
  2. 切换静默失效:bench.ps1 里 $GROUP = '国外默认',PowerShell 5.1 按 ANSI 读 UTF-8 无 BOM 的 .ps1,中文变成乱码,Set-MihomoProxy 切的是不存在的组。不报错,只是 original node: 打印为空。转成 UTF-8 with BOM 后正常。
  3. chunked 解码越界:mihomo /proxies 走 chunked,chunk 长度是字节数,我在已 UTF-8 解码的字符串上按该长度 Substring,中文 1 字符 = 3 字节,直接抛 ArgumentOutOfRangeException。改成字节级去帧、最后统一解码。

落地

  • 运行态:国外默认 → 香港03(经 mihomo API)。
  • 持久化:Clash Verge 不会把外部 API 的改动写回磁盘,profiles.yaml 仍记着日本01,重启即回滚。手动改 line 47 并备份为 profiles.yaml.bak-before-hk03(保持原文件的无 BOM 编码)。
  • 残留风险:Verge 进程退出时有可能用内存状态覆盖该文件,已告知用户「若复发,UI 里手点一次香港03」。

一句话总结

ping 通证明不了链路可用——在 fake-ip 模式下它连出网都没发生;而选节点也不能看延迟,Clash 内置延迟测试只测握手,本次带宽垫底(2.72 Mbps)的那个节点,延迟排第 8、看起来完全正常。

五条可复用 insight

1. fake-ip 模式下的 ping 是自问自答 ⚠️首次

enhanced-mode: fake-ip 时,代理给每个域名发一个 198.18.0.0/16 段的本地虚拟地址,ICMP 由本机应答。所以:

  • ping 任何域名都秒通、延迟接近 0;
  • 节点完全挂掉,ping 结果一模一样。

ping 通在这里不是「网络通」的证据,而是「代理接管了 DNS」的证据。 用户拿它当健康信号,恰恰因为它永远是绿的。

判据:诊断信号必须打在真实链路上。 本次有效的信号是 curl -w 的 time_appconnect(TLS 握手,含代理 CONNECT 隧道 + 远端握手)和 speed_download。看到 198.18.x.x 就该知道 ping / tracert / nslookup 这一层全部失去意义。

已有的 ChatGPT_Windows桌面版安装排障与账号合规边界_v1 提过「fake-ip 会让 DNS 返回保留网段,这是内部映射不等于 DNS 泄露」——本篇是同一现象的另一面:它同时让所有基于 IP 的连通性测试失效。

2. 延迟不等于带宽,内置延迟测试选不出能看视频的节点 ⚠️首次

Clash 的「延迟测试」测的是一次 generate_204 的握手往返,不测吞吐。本次两者几乎不相关:

节点延迟排名带宽排名
日本018 / 3414 / 14(垫底)
日本105 / 3413 / 14
印度0110 / 344 / 14
香港031 / 343 / 14

一个 113ms 的节点可以只有 2.72 Mbps。 如果按 Clash 面板的绿色数字选节点,日本01 排在中上游,永远不会被怀疑。

判据:流媒体 / 大文件场景必须实测吞吐;延迟只用来做第一轮淘汰(筛掉超时的),不用来排序。

3. 探针返回「全 0」时,先怀疑探针 ⭐ 跨项目二次验证

首轮 14 个节点全返回 0 Mbps。这个结果看起来是一个结论(「机场整个废了」),实际是探针自己坏了——请求 200 MB 被 Cloudflare 拒绝,403。

这正是 全站文字截断体检_检测工具本身要先被证伪_v1 那条律的第二次现形,且是跨项目的(上次是浏览器 UI 体检脚本,这次是网络测速脚本):

  • 上次:自制判据里一个拍脑袋的箭头宽度常数,把 8 个正常元素判成缺陷;
  • 这次:探针参数超限,把 14 个正常节点判成全挂。

两次的共同结构是:工具的输出形态很像事实(一张带数字的清单 / 一列整齐的 0),掩盖了「它根本没测成」。

具体判据(本次新增的操作层):0 是一个危险的返回值,因为它同时可能表示「测到了,就是 0」和「压根没测」。 探针必须把区分二者的字段一起打出来——本次是 %{http_code} 和 %{size_download}:code=403 size=1 一眼就和 code=200 size=52428800 分开了。我是在把这两个字段加进 -w 之后,1 次调用就定位的。

推论:任何返回「全部为 0 / 全部失败 / 全部相同」的批量结果,先跑一个已知good的样本自证,再信它。 全体一致的异常,通常是共因,而共因往往就在探针里。

4. 运行态改了 ≠ 配置落盘 ⚠️首次

通过 mihomo API PUT /proxies/{group} 切换节点,立即生效但不落盘。Clash Verge 把选择记在自己的 profiles.yaml 的 selected 字段里,外部改动它不知道,重启后按文件恢复。

我是因为顺手核对了一遍 profiles.yaml,才发现它还写着日本01。如果只验证「现在快不快」,这次修复的寿命就是到下次重启为止——而用户不会把重启和变卡联系起来,只会觉得「上次修好了,又坏了」。

判据:凡是「运行时状态」和「配置文件」两处都存着的设置,改完必须两处都看。 验证问句不是「现在生效了吗」,而是「重启后还在吗」。

(同族:赛事状态机到点不切换_写入方不等于推进方_v1 的「有人写、有人显示,没人推进」——这里是「改了运行态、没人负责写回」。)

5. 非 ASCII 标识符遇上编码不匹配 = 静默走错分支 ⚠️首次

PowerShell 5.1 读 .ps1 默认按系统 ANSI 码页,而绝大多数工具写出的是 UTF-8 无 BOM。后果不是报错,是中文字符串静默变成乱码:

$GROUP = '国外默认'          # 文件里是 UTF-8,PS 5.1 读成乱码
Set-MihomoProxy -Group $GROUP -Name $node   # 切一个不存在的组,API 静默接受

于是脚本一路跑完,14 行结果整整齐齐,每一行都是错的。

三条处置:

  • 写给 PS 5.1 执行的脚本,若含中文,必须存成 UTF-8 with BOM(Out-File -Encoding utf8 在 5.1 下带 BOM;[IO.File]::WriteAllLines 传 UTF8Encoding($true));
  • 或者让脚本本身保持纯 ASCII,中文数据从外部传入(本次候选节点名走 Import-Csv -Encoding UTF8 就没出问题);
  • 命令行内联传参不受影响(走进程参数,是 UTF-8),所以「内联能跑、存成文件就错」是这个坑的典型表征。

顺手教训

  • Clash Verge Rev 默认不开 API 的 TCP 端口。config.yaml 写着 external-controller: 127.0.0.1:9097,但 Get-NetTCPConnection 显示进程只监听 7897;真正可用的是 external-controller-pipe: \\.\pipe\verge-mihomo。用 NamedPipeClientStream 手写 HTTP/1.1 即可打通(/proxies 列组、PUT /proxies/{组名} 切换、/group/{组名}/delay 批量测延迟、/rules 查路由):

    $pipe = New-Object System.IO.Pipes.NamedPipeClientStream('.','verge-mihomo','InOut')
    $pipe.Connect(5000)
    $req = "GET /proxies HTTP/1.1`r`nHost: localhost`r`nAuthorization: Bearer <secret>`r`nConnection: close`r`n`r`n"
    $rb = [Text.Encoding]::UTF8.GetBytes($req)
    $pipe.Write($rb,0,$rb.Length); $pipe.Flush()
    # 读到流关闭 → 按 byte[] 切掉 header、按 byte[] 做 chunked 去帧 → 最后统一 UTF8.GetString

    两个必须踩对的点:组名含中文要 [uri]::EscapeDataString;响应是 chunked,去帧必须在 byte[] 上做(见下条)。

  • HTTP chunked 的长度是字节数,不是字符数。凡是要按长度切分的解码,一律在 byte[] 上做完再统一 UTF8.GetString,不要在已解码的字符串上切——中文/emoji 一定越界。

  • 先排除「便宜的怀疑」再做贵的测量。本次在跑 14 个节点的测速(约 700 MB 流量、4 分钟)之前,先花了两次只读调用排除了流量超额和规则错配。若跳过这步,测完发现是限速,全部白测。

  • curl -w 的 %{exitcode} 在当前 curl 版本不存在,会打印 unknown --write-out variable。要判成败用 %{http_code} + $LASTEXITCODE。

  • 测量方法的局限要写进结论:50 MB 上限对高速节点区分度不足(133 Mbps 意味着 3 秒下完就结束了),同一节点多次测量在 100–140 Mbps 间波动。所以「日本03 133 vs 香港03 100」这个差距不足以支撑排序,最终选香港03 靠的是 Coursera 真实链路复测,不是这张表。

下次改进

  • 遇到「能 ping 通但慢」,第一步查 DNS 解析结果是不是 198.18.x.x / 28.0.0.x 这类保留段,是则立刻放弃全部 ICMP 类判断。
  • 写批量测速 / 体检脚本时,-w 里默认带上能区分「失败」与「真值为 0」的字段(http_code、size_download),并先跑一个已知good样本。
  • 改代理类设置后,验证问句统一成「重启后还在吗」,而不是「现在生效了吗」。
  • 给 PS 5.1 的脚本:含中文存 BOM,或纯 ASCII + 外部传数据。

关联文档

类型/平台工程主题/网络排障主题/代理链路