中文 English

别再只跑一次 speedtest:用 dd 把网络测速和稳定性测成一条证据链

发布时间: 2026-08-30 · 阅读量 --
dd 网络测速 Network Speed 稳定性测试 Stability SSH Linux Ubuntu 26.04 Windows 11 macOS 26 运维 故障排查 自动化

先说结论

一次测速软件给出的“最高速度”,只能回答某个时刻、某条线路、某个服务端的一个瞬间。要判断一条链路能不能稳定承载备份、远程桌面、容器拉镜像或 Agent 调用,最好用自己能控制的两端,反复发送固定大小的数据,并同时记录方向、耗时、退出码、波动和错误原因。dd 正好能把数据量钉死,ssh 负责把字节送过网络,另一端的 dd of=/dev/null 负责接住后立即丢弃。

本文把 dd | ssh | dd 变成一套可复用的网络实验:先解释为什么“跑一次”不够,再展示上传、下载、长稳和故障判读;最后给出 Windows 11、Ubuntu 26.04、macOS 26 三套一键脚本,以及人工执行和 Agent 自动配置两种方法。脚本默认小流量、只写 /dev/null,不依赖第三方测速服务。文中的实测数字来自脱敏的隔离实验,不是任何线路的 SLA 承诺。

原创封面:dd、SSH 与远端 /dev/null 组成网络测速和稳定性实验。

图 1:原创 SVG 封面。核心思想只有一句话:速度是一个数字,稳定性是一组分布。

一、问题背景:为什么“测速 800 Mbps”仍然会卡

1.1 一次测速像只看了一秒钟的天气

很多人遇到网络问题时,第一反应是打开测速网页,看到一个很大的数字,然后下结论:“带宽没问题,肯定是应用的问题。”这一步并没有错,只是证据不完整。

测速网页通常把数据送到它选择的服务端,路径、并发连接、协议、服务端负载和浏览器实现都由它决定。你看到的是“这台电脑到那个服务端,在这一刻,用那套协议的成绩”。而真正让你烦恼的业务,可能走的是另一条路径:备份去某个存储节点,远程终端走一条长延迟隧道,Agent 的模型请求还要经过代理、TLS 和重试。

打个比方:你在小区门口测到水管瞬时流量很大,不代表把水送到顶楼、连续接水半小时也不会忽大忽小。网络也有“水压”:平均速度、方向不对称、瞬时排队、重传和连接建立时间,都会影响实际体验。

1.2 dd 为什么能帮忙

dd 原本是一个字节搬运工:从 if(input file)读数据,按 bs(block size)分块,读 count 个块,再写到 of(output file)。/dev/zero 会源源不断地产生零字节,/dev/null 会把收到的字节吃掉。因此我们可以把它放在网络管道两端:

原创流程图:本地 dd 产生固定字节流,经 SSH 到远端 dd 后丢弃。

图 2:原创图。测试不会在远端留下大文件,但测到的是“本机生产数据 → SSH 加密 → 网络路径 → 远端接收”的端到端时间。

本地 dd if=/dev/zero  ── stdin/stdout ──>  ssh  ──>  远端 dd of=/dev/null

它的优点是简单、可审计、两端都能看见退出码;缺点也要说清楚:这不是裸 TCP 线速仪,它包含 SSH 握手、加密、远端 shell 和两端 CPU 的开销。正因为如此,它更接近“这条 SSH 通道能给我的应用多少吞吐”,而不是宣传页上的理论带宽。

二、先把 dd 的几个开关弄懂:最容易被忽略的是 iflag=fullblock

2.1 四个参数就是四个量杯

status=progress 很适合现场看进度,但它不是所有 BSD 版本都一致支持,所以脚本把诊断输出重定向掉,自己用墙上时钟计算耗时。真正跨管道时还要加 iflag=fullblock:它告诉 dd,一次 read() 只拿到半个块时不要急着把它算成一个完整记录,继续读到块填满。

如果没有这个选项,管道可能出现这样的错觉:本来以为发送了 4 MiB,实际上因为一次只读到 64 KiB,count=4 很快就结束了。数据量变小,速度数字反而变得“漂亮”。这就像规定“数四杯水”,却把四个半杯也当成四杯,账面完成得很快,水桶却没装满。

本地基线也要使用完整块:

dd iflag=fullblock if=/dev/zero bs=1048576 count=32 \
  of=/dev/null 2>/dev/null

2.2 四只时钟决定一个结果

一次 dd 传输至少同时包含四段时间:产生/读取数据、建立 SSH、真正在线路上传输、远端进程接收。把它们混成一个数字没关系,但报告里必须说明定义,否则别人无法复现。

原创信息图:payload、SSH 建连、线路传输、远端 sink 四只时钟共同组成墙上时间。

图 3:原创图。小 payload 时,握手时间占比很大;大 payload 更接近持续吞吐,但会消耗更多流量。

三、测试设计:先做基线,再测两个方向,最后看分布

3.1 三种测试回答三种问题

原创对照表:本地基线、上传、下载分别对应不同的数据路径。

图 4:原创图。上传快不代表下载快,单向结果不能替代双向结果。

测试 数据路径 主要回答的问题
本地基线 dd → /dev/null 本机产生和丢弃字节的上限是多少?
上传 本地 dd → SSH → 远端 dd 客户端到服务端的路径怎样?
下载 远端 dd → SSH → 本地 dd 服务端到客户端的路径怎样?

3.2 单位别混:MiB 不是 MB,Mbps 也不是 MiB/s

本文用 MiB(2²⁰ 字节)计算,因为 bs=1048576 很明确。网络设备常用十进制 Mbps(10⁶ bit/s)。换算时先把 MiB/s × 8 得到 Mib/s,再根据需要换成 Mbps;不要把 100 MiB/s 直接写成 100 Mbps。测速报告至少写出 payload、方向、墙上耗时和单位。

3.3 固定 payload,重复 5~10 轮

只跑一轮无法分辨“偶然排队”和“长期波动”。建议从 16 或 32 MiB、5 轮开始;链路允许时再把 payload 提到 64 或 128 MiB。总流量大约是:

总流量 ≈ payload × 轮数 × 方向数

例如 32 MiB × 5 轮 × 2 个方向,就是约 320 MiB(不含 SSH 协议开销)。先征得链路所有者同意,别在共享出口上做“压力测试”。

原创循环图:同一 payload 重复多轮,记录秒数、退出码,再计算平均值、极值和 CV。

图 5:原创图。稳定性不是“最后一次成功”,而是整组样本的形状。

四、手工跑一轮:两端都不落盘

4.1 先确认远端真的有 dd

远端只需要一个可登录的 POSIX shell、dd/dev/null。先做只读检查,不要上来就发大流量:

ssh -T -o BatchMode=yes -o Compression=no \
  testuser@test-endpoint \
  'command -v dd && command -v sh'

首次连接应人工核对主机指纹。代码里的 testuser@test-endpoint 只是占位符,公开文章和脚本里不放真实地址、机器名或密钥。

4.2 上传:本地生产,远端接收

/usr/bin/time -p sh -c \
  'dd iflag=fullblock if=/dev/zero bs=1048576 count=64 2>/dev/null |
   ssh -T -o BatchMode=yes -o Compression=no \
     testuser@test-endpoint \
     "dd iflag=fullblock of=/dev/null bs=1048576 count=64 2>/dev/null" \
   >/dev/null'

time 打印的 real 就是这一次的墙上耗时。因为块大小固定为 1 MiB,粗略吞吐是 64 ÷ real MiB/s。若退出码不是 0,先看 SSH 错误,不要把失败强行写成“0 MiB/s”。

4.3 下载:远端生产,本地接收

/usr/bin/time -p sh -c \
  'ssh -T -o BatchMode=yes -o Compression=no \
     testuser@test-endpoint \
     "dd iflag=fullblock if=/dev/zero bs=1048576 count=64 2>/dev/null" |
   dd iflag=fullblock of=/dev/null bs=1048576 count=64 2>/dev/null'

这两个方向一定要分开跑。很多家庭宽带、VPN、云主机安全策略和 Wi-Fi 节能机制都会造成上下行不对称。

4.4 最小重复循环

不想马上下载完整脚本,可以先用下面的 Bash 片段。它使用 pipefail,任一端失败都会让这一轮失败:

set -o pipefail
for round in 1 2 3 4 5; do
  start=$(perl -MTime::HiRes=time -e 'printf "%.6f", time')
  dd iflag=fullblock if=/dev/zero bs=1048576 count=32 2>/dev/null |
    ssh -T -o BatchMode=yes -o Compression=no \
      testuser@test-endpoint \
      'dd iflag=fullblock of=/dev/null bs=1048576 count=32 2>/dev/null' \
    >/dev/null
  rc=$?
  end=$(perl -MTime::HiRes=time -e 'printf "%.6f", time')
  seconds=$(awk -v s="$start" -v e="$end" 'BEGIN { print e-s }')
  rate=$(awk -v s="$seconds" 'BEGIN { print 32/s }')
  printf 'upload round=%d rc=%d seconds=%.3f MiB/s=%.1f\n' \
    "$round" "$rc" "$seconds" "$rate"
done

五、一次真实实验:平均值不错,但上传波动更大

我在一条经过授权的隔离 SSH 路径上,用 32 MiB payload 各跑 5 轮。地址、用户名和机器信息均已从截图中移除;Compression=no,两端都用 /dev/null,所以结果不代表磁盘性能,也不代表互联网 SLA。

5.1 本地基线:生产者不是瓶颈

真实终端截图:本地 dd 生产者/接收端基线,使用 fullblock,32 MiB 三轮均成功。

图 6:真实终端输出的脱敏截图。本地基线约 4,043~5,173 MiB/s,远高于后面的 SSH 路径,说明这次实验主要观察网络和加密通道。

5.2 上传:平均约 38.0 MiB/s,CV 16.7%

真实终端截图:本地 dd 经 SSH 上传到远端 dd,五轮全部成功但速度有明显散布。

图 7:真实终端输出的脱敏截图。五轮为 43.5、33.6、42.8、27.6、42.4 MiB/s;最慢与最快相差约 36.6%。

5.3 下载:平均约 68.9 MiB/s,CV 4.7%

真实终端截图:远端 dd 经 SSH 下载到本地 dd,五轮速度较集中。

图 8:真实终端输出的脱敏截图。五轮为 70.7、72.4、70.7、63.2、67.8 MiB/s;这条样本的下载方向明显比上传稳定。

5.4 汇总:零失败不等于没有长尾

真实终端截图:五轮上传/下载汇总,包含平均值、极值、CV 和失败数。

图 9:真实终端输出的脱敏截图。两方向共 10 轮没有传输失败,但上传 CV 明显高于下载;这只是一个时间窗口,不能替代不同时间段的复测。

同一实验还做了 5 次 ICMP 探测:丢包率为 0%,平均 RTT 约 104 ms,但最大 RTT 突然达到约 519 ms。这个组合很典型:没有丢包,不代表没有排队长尾。远程终端卡顿、语音断续或 Agent 首字延迟变大,往往先表现为尾延迟,而不是 ping 直接丢包。

六、怎样读结果:把“快不快”拆成四个问题

原创判读图:速度用 MiB/s,稳定性用 CV、退出码和失败原因共同描述。

图 10:原创图。像看体检报告一样看测速:一个平均值不能替代方向、波动和错误分类。

  1. 吞吐payload / wall-clock seconds。先比较同一 payload、同一方向,再谈不同链路。
  2. 波动:记录 min、max、平均值和 CV(标准差 ÷ 平均值)。CV 高说明这组样本不稳定,但不自动说明根因。
  3. 可靠性:看每轮退出码、SSH stderr 和是否提前结束。rc=0 是必要条件,不是质量证明。
  4. 体验长尾:把 ping 的 P95/P99、SSH 建连时间、应用首字延迟放到同一时间轴。速度高但长尾大,交互仍会卡。

6.1 常见根因与下一步

真实终端截图:故意使用关闭端口得到 Connection refused,同时保留 ping 的 RTT/丢包上下文。

图 11:真实终端输出的脱敏截图。连接失败不能悄悄转换成 0 MiB/s;把错误分类保留下来,排障才有方向。

七、安全边界:测速脚本最怕“顺手写满磁盘”

7.1 of=/dev/nullof=./test.bin 是两回事

网络测速只需要测字节流,不需要留下文件。不要把远端 sink 改成当前目录,也不要在生产盘上加 conv=fsync 伪装网络测试;那会把磁盘写入、文件系统缓存和网络混在一起,还可能把空间打满。若确实要测“网络 + 落盘”,请另写一个有容量上限、可清理、得到授权的磁盘实验。

7.2 四条护栏

原创安全清单:/dev/null、流量预算、主机信任和可中止的 deadline。

图 12:原创图。测试前先问“我是否有权限、最坏会产生多少流量、失败时怎样停”。

  1. 先获授权:只测自己拥有或明确获准的两端;共享出口不要随意跑长时间大 payload。
  2. 设流量上限:脚本把单轮 MiB 和轮数都限制在合理范围,先 dry-run,再执行。
  3. 保留 SSH 安全性:使用密钥和 BatchMode=yes,不要把密码、私钥或跳板凭据写入脚本;首次 accept-new 后仍应核对指纹。
  4. 能停下来:保留连接超时和 keepalive;看到异常波动就停止,不要用并发把问题放大。

八、三套一键脚本:Windows 11、Ubuntu 26.04、macOS 26

完整脚本已经随文章放进仓库,可直接下载:

三套脚本的行为保持一致:默认 16 MiB、每个方向 5 轮;远端和本地都把字节送进 /dev/null;使用 iflag=fullblock 防止短读;打印每轮 secondsMiB/s、退出码和最终 CV;任何一轮失败,脚本返回非零退出码。

8.1 Ubuntu 26.04

chmod +x dd-network-test-ubuntu2604.sh
./dd-network-test-ubuntu2604.sh \
  --remote-host test-endpoint \
  --remote-user testuser \
  --size-mib 32 --rounds 5 --direction both

先看计划、不发字节:

./dd-network-test-ubuntu2604.sh --remote-host test-endpoint --dry-run

脚本只使用系统已有的 bashddsshawk,计时优先使用系统 Perl,找不到时才回退到 Python 标准库;没有调用第三方测速 API。

8.2 macOS 26

chmod +x dd-network-test-macos26.zsh
./dd-network-test-macos26.zsh \
  --remote-host test-endpoint \
  --remote-user testuser \
  --size-mib 32 --rounds 5 --direction both

macOS 版本使用系统 zsh 和 OpenSSH,不要求安装 WSL 或额外测速软件。若本机 dd 报告不认识 iflag=fullblock,先升级系统或在经过审核的环境里使用兼容的 GNU coreutils;不要直接删掉该选项后把短读当成完整块。

8.3 Windows 11

Windows 原生没有 /dev/zero,所以 PowerShell 脚本用 .NET 生成零字节并写入 Windows 自带的 ssh.exe 标准输入,远端仍由 dd 接收;下载方向则读取 SSH 标准输出后丢弃,不需要 WSL,也不会创建临时文件。

Set-ExecutionPolicy -Scope Process Bypass
& .\dd-network-test-windows11.ps1 `
  -RemoteHost test-endpoint `
  -RemoteUser testuser `
  -SizeMiB 32 -Rounds 5 -Direction both

如果系统没有 OpenSSH Client,先在“可选功能”中启用微软提供的组件;脚本不会替你下载来路不明的 dd.exe

真实终端截图:Ubuntu、macOS 脚本语法检查通过,PowerShell 结构检查通过;PowerShell 需在 Windows 上做最终运行验收。

图 13:真实检查输出的脱敏截图。当前写作环境是 macOS,因此 PowerShell 做了静态结构检查;拿到 Windows 11 后还应执行一次小 payload 的真实回环。

九、人工自动执行 vs Agent 自动配置

9.1 人工自动执行:先 dry-run,再放行流量

  1. 从上面的链接下载对应脚本,检查文件哈希和内容;
  2. 填入你有权限测试的 endpoint、账号和 SSH 端口;
  3. 运行 --dry-run,确认 payload、轮数、方向和总流量;
  4. 手工核对主机指纹,再执行正式测试;
  5. 保存 stdout、stderr、日期、时区、客户端/服务端系统版本和链路变更,形成可比较的基线。

人工方式的价值是第一次建立“这条链路平时长什么样”的直觉。不要把脚本输出里的地址和用户名直接贴到公开工单或文章里,先做脱敏。

9.2 Agent 自动配置:给它边界清楚的任务单

你可以把下面的提示词交给已有的 Codex、Claude Code、OpenClaw 或其他 Agent。它要求 Agent 先盘点、再预演、最后才发流量:

请在当前机器上建立一份“dd + SSH 网络测速与稳定性基线”,但只操作我明确授权的远端 endpoint。

要求:
1. 先识别操作系统,选择 dd-network-test-windows11.ps1、dd-network-test-ubuntu2604.sh 或 dd-network-test-macos26.zsh;
2. 先检查 ssh、dd、awk/PowerShell 是否存在,打印版本和脚本 SHA-256;
3. 只使用我提供的 RemoteHost、RemoteUser、Port,不猜测、不扫描其他地址;
4. 先执行 dry-run,计算 payload × rounds × directions 的最坏流量,等待我确认;
5. 正式测试使用小 payload、BatchMode、Compression=no、iflag=fullblock,远端和本地都写 /dev/null;
6. 每轮记录方向、秒数、MiB/s、退出码和 stderr,失败立即停止,不把失败伪装成 0 MiB/s;
7. 完成后计算 avg/min/max/CV/失败数,并把原始日志中的 IP、完整主机名、用户名、路径和密钥全部脱敏;
8. 不修改防火墙、SSH 配置、路由、内核参数,不安装第三方测速服务;
9. 报告“测到了什么、没测到什么、下一步需要谁批准”,不要把结果写成 SLA 承诺。

Agent 自动化的边界要写在任务单里:它可以替你重复测量、整理证据和比较历史,但不能替你猜 endpoint、绕过主机指纹确认或擅自扩大流量。

十、常见问题(Q&A)

Q1:dd 测的是互联网带宽吗?
不一定。它测的是你指定的两端之间、通过 SSH 的一条具体路径。要测公网,必须在你拥有或获准使用的公网两端部署接收端;不要把陌生机器当测速服务器。

Q2:为什么浏览器测速比 dd 快很多?
浏览器测速常用多并发连接、专门协议和就近服务端;本文是一条 SSH 单通道,还包含握手和加密。两者回答的问题不同,不能直接比较数字。

Q3:iflag=fullblock 为什么这么重要?
管道读取可能返回短块,count 默认数的是读取记录,不保证总字节数。fullblock 让每个记录尽量填满,避免少发数据却得到虚高速度。

Q4:一定要 root 才能跑吗?
不需要。只要 SSH 账号能执行 dd、访问 /dev/zero/dev/null 即可。更推荐最小权限账号,避免用 root 做普通测速。

Q5:Windows 为什么没有安装一个 dd.exe
为了减少供应链和兼容风险,脚本使用系统 ssh.exe 加 .NET 字节流,只有远端执行 dd。如果你确实要本地 GNU dd,请从可信的软件源单独审核,不要下载同名未知二进制。

Q6:跑 5 轮都成功,能说网络稳定吗?
只能说这 5 个样本没有失败。还要看速度 CV、RTT 长尾、不同时间段和不同方向;稳定性是长期分布,不是一张绿色截图。

Q7:为什么建议 32 MiB,而不是 1 GiB?
32 MiB 足以压过一次 SSH 握手的相对影响,同时不会在共享链路上制造太多流量。先小后大,按授权和预算逐步增加。

Q8:能不能把 of=/dev/null 改成文件,顺便测磁盘?
可以做另一个实验,但那已经是“网络 + 文件系统 + 磁盘”的混合测试。请单独设容量上限、清理策略和维护窗口,别把它混进网络基线。

Q9:为什么不用 iperf3
iperf3 更适合测 TCP/UDP 线速、并发和丢包;dd + SSH 的优势是几乎所有 Unix 服务器都已有,测到的路径更像真实 SSH/文件传输。两者可以互补,不必互相替代。

Q10:看到 Connection refused,是不是带宽为零?
不是。它说明目标端口没有接受连接,可能是端口、服务或防火墙问题。保留错误文本,先修可达性,再谈吞吐。

十一、写在最后:把“感觉卡”变成可以复查的证据

dd 最有价值的地方,不是它能打印一个很大的 MiB/s,而是它迫使我们把问题说清楚:谁产生数据、谁接收数据、走哪个方向、发多少、花多久、失败在哪里、波动多大。只要两端是你能控制且获准测试的机器,就能在不依赖第三方测速服务的前提下,建立自己的基线。

我的建议是把这套实验放进变更流程:网络、VPN、路由、云主机规格或 SSH 加密策略变更前后,各跑一组小 payload;把原始日志和脱敏汇总一起存档;当远程桌面或 Agent 变慢时,先复测同一方向,再决定是否改 MTU、拥塞控制或应用重试。先把证据链补齐,再动参数。

参考资料GNU Coreutils dd 文档OpenBSD dd 手册OpenSSH ssh 手册Microsoft OpenSSH for WindowsUbuntu dd manpageRFC 1122:Internet 主机通信要求。文中的命令和数字以写作时的系统版本与隔离实验为准,执行前请根据自己的系统和授权范围复核。

本文阅读量 --