bigywater.tech
网络协议 · 79

Range 请求与断点续传

Range 让客户端只取资源的一部分:断了可以接着下,大文件可以多线程一起下。这篇在真实服务器上把 Range 的各种写法、416、If-Range 逐个试一遍,实际做一次断点续传并校验哈希,再用 netlab 造一条 100 ms 往返的链路,看并行分段下载到底快多少。

9770 字节
断点续传时第二次只传的字节(完整 15770,已有 6000)
416
起点超出资源时的状态码,同时 Content-Range: bytes */15770 告知总大小
2.5 → 15.6 MB/s
窗口受限的 100 ms 链路上单连接与 8 条连接并行分段下载的吞吐

基本用法

实测 · 本机执行输出

Range: bytes=a-b → 206 + Content-Range: bytes a-b/总大小;后缀写法 bytes=-n 取最后 n 字节;终点超出会被截断。

416 与忽略

实测 · 本机执行输出
  Range: bytes=0-99                            → 206,Content-Range = bytes 0-99/15770,Content-Length = 100,收到 100 字节,内容一致
  Range: bytes=1000-1999                       → 206,Content-Range = bytes 1000-1999/15770,Content-Length = 1000,收到 1000 字节,内容一致
  Range: bytes=-100(最后 100 字节)                 → 206,Content-Range = bytes 15670-15769/15770,Content-Length = 100,收到 100 字节,内容一致
  Range: bytes=15270-(从某处到结尾)                  → 206,Content-Range = bytes 15270-15769/15770,Content-Length = 500,收到 500 字节,内容一致
  Range: bytes=0-31540(终点超过大小)                 → 206,Content-Range = bytes 0-15769/15770,Content-Length = 15770,收到 15770 字节,内容一致
  Range: bytes=15780-15790(起点超出资源)→ 416,Content-Range = bytes */15770,消息体 0 字节
  Range: bytes=0-99,200-299,1000-1099(三段)→ 200,Content-Type = image/png,消息体 15770 字节;开头 b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02D\x00\x00\x00\xa4\x08\x06\x00\x00\x00v;1\xf1\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00 \x00ID'
  Range: items=0-5(不认识的单位)→ 200,消息体 15770 字节

起点超出 → 416 并告知总大小;多段范围和未知单位这台服务器直接返回 200 完整内容。

If-Range

实测 · 本机执行输出
  If-Range: <当前 ETag> + Range → 206,收到 100 字节
  If-Range: "old-etag"(资源已变)+ Range → 200,收到 15770 字节(服务器忽略 Range,返回完整新资源)

ETag 匹配才给片段,不匹配返回完整新资源,防止拼出损坏的文件。

续传

实测 · 本机执行输出
  第一次:状态 200,只读了 6000 字节就断开(完整大小 15770)
  续传:Range: bytes=6000- → 206,Content-Range = bytes 6000-15769/15770,收到 9770 字节;拼起来共 15770 字节,SHA-256 前 12 位 9c121e619bfe,与完整下载一致
  对比从头重来:要再传 15770 字节;续传只传了 9770 字节,省了 38%(这里被省掉的只是 6000 字节)

读 6000 字节中断后 Range: bytes=6000- 取剩下的,拼接与完整下载的 SHA-256 一致。

并行下载

实测 · 本机执行输出
  1 条连接:3.23 秒,2.5 MB/s,拼接结果正确
  4 条连接:0.91 秒,8.8 MB/s,拼接结果正确
  8 条连接:0.51 秒,15.6 MB/s,拼接结果正确
  4 条连接比 1 条快 3.5 倍,8 条比 1 条快 6.3 倍(窗口受限的单条连接吞吐 ≈ 256 KiB ÷ 100 ms = 2.6 MB/s;多连接相当于把窗口加起来)

窗口受限的链路上,4 / 8 条连接分段并行比单连接快 3.5 / 6.3 倍。

分步演示

Range 与断点续传
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一至四节是对 www.python.org 的 HTTPS 请求(Accept-Encoding: identity);第五节是本机脚本服务器 + netlab(每方向 50 ms),每个连接数只下载 2 次取平均;多段范围被忽略是这一台服务器的行为,别的服务器可能返回 multipart/byteranges
    • 没有验证:multipart/byteranges 响应的解析(没有遇到这样的服务器);Range 对压缩内容(Content-Encoding)的含义;Range 请求在缓存和 CDN 里的处理;大文件(GB 级)并行下载的最佳连接数;服务器对同一个 IP 并发连接数的限制
    京ICP备2026067502号-1京公网安备11010502063487号