断点续传需手动构造range头并校验服务端响应,用fopen("r+b")+fseek精准写入,分段下载须独立临时文件合并,禁用自动重试并持久化断点状态。

HTTP Range 请求必须手动构造,curl 默认不启用断点续传
很多开发者以为用 curl_easy_setopt 设置了 CURLOPT_RESUME_FROM 就万事大吉,其实这只是起点。真正起作用的是 HTTP Range 头,而 CURLOPT_RESUME_FROM 只是让 libcurl 自动加这个头——前提是服务端支持且返回 206 Partial Content。如果服务端忽略 Range 或返回 200 OK,下载就从头开始。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 下载前先发一个 HEAD 请求,检查响应头是否含
Accept-Ranges: bytes或Content-Range - 若文件已存在,用
stat()获取本地文件大小,作为续传起点传给CURLOPT_RESUME_FROM - 务必设置
CURLOPT_HTTPHEADER手动追加"Range: bytes=xxx-"(尤其在自定义 HTTP 客户端时) - 收到响应后,检查状态码:不是
206就得清空本地文件重下,否则写入会错位
fseek() 和 fwrite() 的偏移与追加模式要严格匹配
本地文件续传的本质是“跳过已有字节,从末尾继续写”。但 C++ 标准库的 std::ofstream 默认打开是覆盖模式,ios::app 又强制写到末尾、无法指定偏移——这和断点续传要求矛盾。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用 C 风格
fopen(filename, "r+b")打开文件(可读可写,不截断) - 调用
fseek(fp, offset, SEEK_SET)定位到断点位置,再用fwrite()写入新数据 - 避免用
std::ofstream的seekp()+write()组合,某些平台(如 Windows)下缓冲区行为不稳定 - 写入后记得
fflush(fp),防止断电或崩溃导致最后几 KB 丢失
多线程并发下载同一文件时,fseek/fwrite 不是原子操作
想靠分段下载加速?别直接多个线程往同一个文件句柄里写。即使每个线程都 fseek 到不同偏移,fwrite 仍可能因内核缓冲、文件系统缓存或线程调度顺序错乱,导致数据覆盖或空洞。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 分段下载必须为每段分配独立临时文件(如
part_001.tmp,part_002.tmp),完成后用rename()合并 - 合并阶段才用单线程顺序读取各分段、
fseek定位写入目标文件 - 不要依赖
lseek()+write()的原子性——POSIX 不保证跨线程安全,Linux 下也仅对小于 PIPE_BUF 的写入有原子性保障 - 若必须单文件直写,至少用
flock()加文件锁,但会显著降低并发收益
服务器返回 Connection: close 时,libcurl 可能静默重连并丢弃 Range
有些 CDN 或反向代理在长连接关闭后,libcurl 会自动重试请求,但默认不重发原始 Range 头——结果就是第二段请求变成全量下载,本地文件被重复写入开头,内容彻底损坏。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 显式禁用自动重试:
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L)+ 捕获CURLE_PARTIAL_FILE错误 - 手动处理连接中断:监听
CURLOPT_XFERINFOFUNCTION,若传输卡住超时,主动 cleanup 并重建 handle - 每次重试前重新设置
CURLOPT_RESUME_FROM和CURLOPT_HTTPHEADER,不能依赖上一次配置残留 - 记录已成功写入的字节数到外部状态文件(非内存变量),避免进程重启后丢失断点位置
断点续传真正的复杂点不在协议层,而在状态一致性:网络不可靠、磁盘 I/O 不可预测、服务端行为不统一。最容易被忽略的是“本地文件大小”和“服务端实际可续传位置”的校验——哪怕只差 1 字节,后续所有写入都会错位,且这种错误往往到合并或校验时才暴露。










