HTTP Range请求有效断点续传需先发HEAD请求确认服务端支持Accept-Ranges: bytes,再发GET并设置Range头(如bytes=1024-),响应必须为206且含Content-Range,客户端须精确seek写入并双重校验偏移与文件大小。

HTTP Range 请求怎么发才有效
断点续传本质是让服务器只返回文件的一部分,核心依赖 HTTP 的 Range 请求头。但不是所有服务器都支持,也不是加了头就一定返回 206 Partial Content——必须先确认服务端支持 Accept-Ranges: bytes 响应头。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 下载前先发一次 HEAD 请求,检查响应头是否含
Accept-Ranges: bytes;若为none或缺失,说明不支持断点续传,只能全量重下 - 实际下载时用
Range: bytes=1024-(从第 1024 字节起)或Range: bytes=1024-2047(指定区间),注意字节偏移从 0 开始 - 收到响应后务必校验状态码:必须是
206,200说明服务器忽略 Range,需中止并报错 - C++ 中用 libcurl 时,设
curl_easy_setopt(handle, CURLOPT_RANGE, "1024-")即可,无需手动拼 Header
本地文件如何安全追加写入而不损坏
直接 fopen(..., "ab") 看似简单,但并发写、异常中断、磁盘满等场景下极易导致文件末尾数据错位或覆盖。关键不是“追加”,而是“精确写入指定偏移”。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
fseek(fp, offset, SEEK_SET)定位,再fwrite()—— 必须确保fseek成功且返回 0,否则后续写入位置不可控 - Windows 下需额外调用
_setmode(_fileno(fp), _O_BINARY),否则文本模式会把\n转成\r\n,破坏二进制文件校验 - 写入前建议先用
truncate()或_chsize_s()预分配文件大小(尤其大文件),避免反复扩容导致性能抖动 - 每次写完立即
fflush(fp),防止缓冲区未落盘而进程崩溃丢数据
如何可靠记录和恢复下载进度
进度不能只存在内存里,重启就丢;也不能每次写都同步刷磁盘(太慢);更不能把进度存进正在写的下载文件里(易被截断)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 单独建一个元数据文件,如
file.zip.partinfo,内容只需一行 JSON 或纯文本:{"offset": 1234567, "total_size": 987654321} - 更新进度时,先写新文件(如
.partinfo.tmp),fsync()后rename()原子替换,避免写到一半崩溃导致脏数据 - 启动恢复时,先读
.partinfo,再用stat()校验对应下载文件的实际大小是否匹配offset,不一致则清空重来 - 别用时间戳或哈希值做恢复依据——小文件可能秒级完成,时间精度不够;哈希要全量计算,开销大且无法增量验证
libcurl 多线程环境下如何避免 Range 请求错乱
多个下载任务共用一个 CURL handle 会导致 Range 头残留或覆盖;而每个任务新建 handle 又浪费连接池。真正的问题是共享状态没隔离。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每个下载任务必须独占一个
CURL*handle,哪怕只下载一个文件也别复用 - 设置
CURLOPT_TCP_KEEPALIVE和CURLOPT_TCP_KEEPIDLE保持连接复用,但不要跨任务复用 handle - 如果要用多线程并发下载分片(如把大文件切 4 段并行),每段必须独立 set
CURLOPT_RANGE,且写入文件时用pwrite()(Linux)或SetFilePointerEx() + WriteFile()(Windows)保证偏移安全 - libcurl 默认启用
HTTP/1.1,但某些 CDN 会把多个 Range 请求合并响应,此时需检查响应头Content-Range是否与请求一致,不一致立刻失败
最麻烦的其实是服务器行为不规范:有的返回 206 却不带 Content-Range,有的对同一 Range 返回不同长度数据。这些没法靠客户端逻辑兜底,只能日志记录、人工介入或降级为全量下载。


















