断点续传核心是正确使用HTTP Range头并严格校验服务器响应。需先HEAD检查Accept-Ranges,用libcurl的CURLOPT_RANGE设置范围,解析Content-Range响应头确认实际返回区间,以r+b模式定位写入,预分配文件大小,原子化持久化偏移状态,416时降级全量下载或清理重试。

如何用 C++ 发起带 Range 的 HTTP 请求
断点续传的核心是让服务器只返回文件的一部分,这依赖于 HTTP Range 请求头。C++ 本身不内置 HTTP 客户端,得靠第三方库(如 libcurl)实现。关键不是“能不能发”,而是“怎么发才被服务器正确响应”。
常见错误:直接拼接 "Range: bytes=1024-" 但没检查服务器是否支持;或用了 Range 却没处理 416 Requested Range Not Satisfiable 错误。
- 必须在请求头中显式设置
Range: bytes=<start>-<end></end></start>(<end></end>可省略表示到末尾) - 发起前先 HEAD 请求,检查响应头是否含
Accept-Ranges: bytes,否则断点续传不可行 - libcurl 中用
curl_easy_setopt(curl, CURLOPT_RANGE, "0-1023")更安全,它会自动加头并处理部分逻辑 - 别忽略
Content-Range响应头——它告诉你实际返回了哪一段,比如bytes 1024-2047/1048576,必须校验start和length是否匹配预期
本地文件写入时如何安全追加并校验偏移
下载中断后重新开始,不能简单 fopen(..., "wb") 覆盖,也不能无脑 "ab" 追加——因为网络可能重复返回同一段、或服务器响应错乱。
正确做法是:以 "r+b" 打开已有文件,fseek() 到目标偏移再写;同时记录已写长度,避免覆盖未完成块。
立即学习“C++免费学习笔记(深入)”;
- 首次下载前创建空文件并用
ftruncate()预分配大小(需从Content-Range或 HEAD 的Content-Length获取总长) - 每次写入前用
fseek(fp, offset, SEEK_SET)定位,再用fwrite()写入,避免指针错位 - 写完立即调用
fflush(fp),防止缓冲区滞留导致断电后丢失最后一段 - 建议每写 64KB 后用
fsync()强刷磁盘(Linux/macOS),Windows 用_commit()
如何持久化断点状态并避免多线程冲突
程序崩溃或用户关闭后,得知道“上次下到哪了”。这个状态不能只存在内存里,也不能靠文件大小反推——因为写入可能中途失败、校验未通过。
最简方案是单独存一个元数据文件(如 file.zip.part),里面只写当前已确认完成的字节偏移。
- 每次成功写入并校验 CRC32 或 MD5 片段后,原子更新元数据文件(先写临时文件,再
rename()覆盖) - 恢复时读元数据 → 截断文件至该偏移 → 从该位置发起
Range请求 - 若用多线程分段下载(如 4 线程各下 1/4),每个线程必须独占自己的偏移区间,且元数据要记录每段的完成状态(例如 JSON:
{"0": 1048576, "1": 1, "2": 0, "3": 1048576}) - 避免用全局变量存偏移量——线程 A 更新了,线程 B 还在用旧值,结果重叠写入
遇到 416 或 206 响应时该怎么处理
206 Partial Content 是理想响应,但现实里常碰到 416:要么服务器不支持 Range,要么你算错了 start(比如超过了文件总大小),或者中间文件被手动删改过。
这时候不能直接报错退出,而要降级为全量重下,或提示用户清理残留文件。
- 收到
416时,检查响应头中的Content-Range: */<total></total>,提取<total></total>;若为空,说明服务器根本不支持断点,改用GET全量下载 - 若
<total></total>小于本地记录的“预期总长”,说明服务器文件已变更(如被更新),应清空本地文件和元数据,重新 HEAD - libcurl 默认把
416当作错误返回,需设置curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0L)才能捕获响应码 - 不要假设
206一定对应你请求的范围——务必解析Content-Range头,比对起始/结束值,不一致就 abort
真正难的不是发一个 Range 请求,而是把偏移管理、文件 I/O、异常恢复、多线程协同这几层逻辑串成一条不掉链子的流水线。尤其注意:服务器返回的 Content-Range 和你本地记录的 offset 必须严格一致,差 1 字节都可能导致后续所有数据错位。



















