大文件同步不直接diff而需分块哈希,因内存与io压力大且只需定位变化块;须用确定性哈希(如sha256、xxhash3)而非std::hash;块大小建议4mb,字节对齐切分;两端需统一编码、换行符等策略以保块边界一致。

为什么直接 diff 文件内容在大文件场景下不现实
因为逐字节比对需要把两边文件全读进内存,10GB 文件就得开 20GB 内存(两边都要加载),IO 和内存压力都扛不住。更关键的是,网络同步时你并不需要知道“第 3.2GB 处多了一行”,而是想知道“哪些块变了、哪些没变”——所以得切块 + 哈希。
std::hash 不适合做增量同步的哈希依据
它不是稳定哈希:同一程序不同运行、不同编译器、甚至不同标准库实现,std::hash<:string></:string> 的结果都可能不一样。增量同步依赖的是跨机器、跨时间一致的摘要值,必须用 MD5、SHA256 或至少是 xxHash3 这类确定性哈希。
- 用
OpenSSL的MD5_Update/SHA256_Final接口,或轻量级库如xxhash(C++ 封装可用XXH3_64bits) - 块大小建议设为
4MB:太小(如 64KB)导致哈希调用频繁、元数据膨胀;太大(如 64MB)会让局部修改触发整块重传 - 注意:哈希计算前要按字节对齐切块,不能按行切——否则换行符差异会导致后续所有块偏移错位
如何用哈希列表识别远端缺失/变更的块
本地算完所有块哈希后,生成一个 std::vector<:array>></:array>(SHA256 是 32 字节),再通过轻量协议(比如纯文本 HTTP POST 或自定义二进制头)发给服务端。服务端拿着这个哈希列表查自己对应文件的块哈希缓存,返回缺失索引和变更索引。
- 服务端缓存不能只存哈希,还得存每个块的
offset和size,否则无法定位该传哪段 - 客户端收到响应后,只读取并发送那些索引对应的原始块(用
pread配合open(..., O_RDONLY),避免 mmap 大文件引发 swap) - 别忘了加校验:传输完每一块后,让服务端立刻用同样哈希算法验证,失败则重传该块,不重传整个文件
rsync 协议逻辑能抄,但别直接用 librsync 绑定 C++ 项目
librsync 是 C 写的,头文件裸露大量 C 风格指针和回调,C++ 里手动管理 rs_sig_t、rs_job_t 容易悬垂或泄露;而且它默认用弱滚动哈希(Adler-32),抗碰撞能力差,小概率导致块误判。
- 真要复用 rsync 思路,只借鉴它的三阶段:签名生成 → delta 计算 → patch 应用;自己用
std::span<const uint8_t></const>拆块 +XXH3_64bits做强哈希 - 网络层别让它参与:librsync 的
rs_delta输出的是二进制 patch 流,可读性差、调试困难,不如直接传原始块 + 索引 - 特别注意:Windows 下文件映射要用
CreateFileMapping+MapViewOfFile,而不是 POSIX 的mmap,否则在大文件上会 silently 失败
真正难的不是算哈希,是让两端对“块边界”达成一致——哪怕只是换了个编辑器保存,CRLF/LF 差异、BOM 插入、末尾空行增删,都会让后续所有块哈希全错。所以生产环境必须约定编码格式、行尾规范、是否 strip BOM,并在元数据里显式记录这些策略标识。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











