断点续传的核心是客户端主动维护已接收字节位置并精确写入文件指定偏移,依赖服务端支持Range请求(返回206状态)与本地元数据持久化(如.offset文件),C++需结合libcurl等网络库及二进制文件流定位(seekp/seekg)实现,避免覆盖写和缓冲干扰。

断点续传的核心是记录和恢复文件偏移量
断点续传不是靠“重试”实现的,而是靠客户端主动维护已接收字节位置。关键在于:每次写入前先 fseek 到上次中断处,且服务端必须支持按 Range 请求部分内容。C++ 标准库本身不提供 HTTP 断点能力,必须配合网络库(如 libcurl)和本地文件状态管理。
常见错误是直接用 fopen("file.bin", "wb") 覆盖写入——这会清空已有内容,彻底丢失进度。正确做法是用 "ab" 模式追加,或用 "r+b" 定位写入,但后者需确保文件已存在且长度足够。
- 先用
stat()或std::filesystem::file_size()获取本地文件当前大小,作为起始偏移 - HTTP 请求头必须带上
Range: bytes=<offset>-</offset>(例如Range: bytes=1024-) - 响应状态码应为
206 Partial Content,而非200 OK;收到其他状态要中止 - 写入时建议用
fseek(fp, offset, SEEK_SET)+fwrite(),避免依赖"ab"的隐式定位(某些平台对大文件追加有缓冲问题)
libcurl 实现 Range 请求与进度回调
libcurl 是最常用选择,它原生支持 Range 和进度监控。重点不在“怎么发请求”,而在“如何把接收到的数据准确落到文件指定位置”。CURLOPT_WRITEFUNCTION 回调里不能直接 fwrite,因为数据块可能跨偏移边界,必须结合当前已写总字节数动态计算写入点。
示例关键片段:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
FILE* fp = static_cast<FILE*>(userdata);
long offset = ftell(fp); // 实际已写入字节数
size_t written = fwrite(ptr, size, nmemb, fp);
return written;
}
<p>// 设置前必须先 fseek 到期望起点
fseek(fp, start_offset, SEEK_SET);</p><p>curl_easy_setopt(curl, CURLOPT_URL, "<a href="https://www.php.cn/link/de829cc41d27f07c17771b5027167353">https://www.php.cn/link/de829cc41d27f07c17771b5027167353</a>");
curl_easy_setopt(curl, CURLOPT_RANGE, "1024-"); // 注意:这里只填后半段,libcurl 自动拼
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
-
CURLOPT_RANGE值填"1024-"即可,libcurl 会自动加Range:前缀 - 不要在回调里调用
fseek——libcurl 传输过程中文件指针可能被内部操作干扰 - 务必检查
curl_easy_perform()返回值;若失败,start_offset就是下次重试起点 - 多线程下每个传输需独立
FILE*和偏移变量,避免共享状态错乱
如何安全保存和读取断点位置
断点信息不能只靠文件大小推断——用户可能手动删改文件、磁盘空间不足导致写入失败、或校验发现部分数据损坏。必须将偏移量、URL、ETag(或文件 hash)持久化到单独的元数据文件(如 .file.bin.resume),否则无法区分“下载完成”和“恰好写满目标大小但中途出错”。
- 元数据格式建议 JSON 或简单键值对,字段至少包含:
offset、url、etag、last_modified - 写入元数据前,先
fsync()或_commit()(Windows)确保落盘,再更新主文件;顺序反了会导致元数据和文件状态不一致 - 恢复时,先读元数据,再
stat()校验本地文件大小是否匹配offset;不匹配则视为断点失效,从头开始 - 不要用
atexit()保存进度——程序崩溃时不会触发;应在每次成功写入后增量更新
并发下载与校验的坑
单文件断点续传看似简单,一旦引入分块并发(如把文件切 4 段并行下载),复杂度陡增。C++ 没有内置原子文件写入支持,多个线程/进程往同一文件不同区域写,极易出现数据错位或覆盖。
- 必须用
lseek()+write()(POSIX)或SetFilePointerEx()+WriteFile()(Windows),避免 stdio 缓冲干扰 - 每块写入前加文件区域锁(
fcntl(F_SETLK)或LockFileEx),否则即使偏移正确,也可能因内核缓存未刷导致脏读 - MD5/SHA256 校验不能等全部写完再算——内存扛不住大文件;应边写边更新哈希上下文,但注意:断点续传时需从完整哈希初始状态开始,而非继承上次中间值
- HTTP/2 多路复用下,多个
Range请求共用连接,但 libcurl 默认不启用,需显式设CURLOPT_HTTP_VERSION = CURL_HTTP_VERSION_2TLS
真正麻烦的从来不是“怎么跳到指定位置写”,而是“怎么让多个写入者互不干扰地落盘,且崩溃后还能精确还原现场”。偏移量只是表象,底层 I/O 控制和元数据一致性才是断点续传的硬骨头。

















