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.36s 6.81s 60 KB/s(785KB 用了 13.0s) api.coursera.org6.80s 7.34s — 视频 CDN 1.70s 2.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 秒窗口):
| 排名 | 带宽 | 延迟 | 节点 |
|---|---|---|---|
| 1 | 133.33 Mbps | 72ms | 🇯🇵 日本03【1.0】 |
| 2 | 133.33 Mbps | 92ms | 🇸🇬 新加坡01【1.0】 |
| 3 | 100 Mbps | 36ms | 🇭🇰 香港03【1.0】 |
| 4 | 50 Mbps | 126ms | 🇮🇳 印度01【1.0】 |
| 5 | 40 Mbps | 123ms | 🇰🇷 韩国01【1.0】 |
| … | |||
| 13 | 5.07 Mbps | 91ms | 🇯🇵 日本10【0.5】 |
| 14 | 2.72 Mbps | 113ms | 🇯🇵 日本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 Mbps | 100–133 Mbps |
| coursera.org TLS | 6.36s | 0.19–0.25s |
| 视频 CDN TTFB | 2.38s | 0.27s |
| 流量倍率 | 2.0 | 1.0 |
Coursera 720p 约需 3 Mbps —— 原节点 2.72 Mbps 连 480p 都紧张,且播放器每取一个分片都要重新握手,每次干等 6 秒。
三个把我带偏的工具故障
- 测速全 0:首轮 14 个节点全部返回 0 Mbps,形似「所有节点都挂了」。实为探针请求
bytes=209715200超出 Cloudflare 单请求上限,返回 403 + size=1。改回 50 MB 后立刻测出 17.5 MB/s。 - 切换静默失效:
bench.ps1里$GROUP = '国外默认',PowerShell 5.1 按 ANSI 读 UTF-8 无 BOM 的 .ps1,中文变成乱码,Set-MihomoProxy切的是不存在的组。不报错,只是original node:打印为空。转成 UTF-8 with BOM 后正常。 - 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 的握手往返,不测吞吐。本次两者几乎不相关:
| 节点 | 延迟排名 | 带宽排名 |
|---|---|---|
| 日本01 | 8 / 34 | 14 / 14(垫底) |
| 日本10 | 5 / 34 | 13 / 14 |
| 印度01 | 10 / 34 | 4 / 14 |
| 香港03 | 1 / 34 | 3 / 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 + 外部传数据。
关联文档
- ⭐ 全站文字截断体检_检测工具本身要先被证伪_v1 —— 本篇 insight 3 是它的跨项目二次验证:那篇是 UI 体检脚本的自制判据把正常元素判成缺陷,本篇是网络测速探针的超限参数把正常节点判成全挂;本篇补上操作层判据「
0同时意味着『测到 0』和『没测成』,探针必须输出能区分二者的字段」 - ⭐ 交付前实测证伪律_v1 —— 本篇全程按它执行:「延迟低所以应该快」是未验证的方案,实测才发现延迟第 8 的节点带宽垫底
- ⚠️ 赛事状态机到点不切换_写入方不等于推进方_v1 —— 同族「状态有人改、没人负责持久化」:那篇是没人在到点推进状态,本篇是没人把运行态写回配置文件
- ChatGPT_Windows桌面版安装排障与账号合规边界_v1 —— 同目录、同一台机器的 Clash 环境;那篇讲 fake-ip 的保留网段不等于 DNS 泄露,本篇讲它同时让所有 ICMP 连通性测试失效
- ⚠️ OpenAI区域封锁与Worker就近执行陷阱_北美DO跳板_v1 —— 同族「出口链路决定一切」:那篇是调用方 IP 决定能否访问,本篇是出口节点带宽决定能否流畅
- ⭐ 构建产物的脏改动不是事故_复发是归因错了的信号_v1 —— 次日同族案例,共同点是最方便取用的那个信号最不可信:本篇是 ping 永远绿(fake-ip 自问自答)导致误判链路健康,那篇是记忆里的归因永远在(“上次就是 Obsidian”)导致误判故障来源;那篇把这条纪律从”工具的输出”和”诊断的信号”扩到第三个信息源——你自己写下的历史结论
- 复盘事实先行原则 —— 本篇顶部事实冻结区按它执行
- 09_平台工程索引 —— 平台工程区入口;本文归入「账号与访问」