rsync增量原理,及C++实现

news2024/11/24 22:38:59

1、目标端将目标文件按700字节为大小分块计算强弱校验值(强:md5  弱:adler32,注:弱校验码计算量小速度快,先比对弱校验码,弱校验值一样再比对强校验码),再结合块号组成一个校验列表发给源端。

2、源端再将这些强弱校验信息利用散列函数存放入hash表(为了快速查询),源端一个字节一个字节逐个偏移文件指针比对块的校验值,组成重组信息发送给目标端。

3、目标端利用重组信息重新生成文件。

代码示例如下, demo中BLOCK_SIZE设置为5方便比较(源码中的默认大小为700),且用的散列函数为相对简单的除余法(源码中的散列函数为其他)。此demo仅本地单进程只处理本地文件,不涉及客户端和服务端。

文件:rsync_test.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <sys/types.h>
#include <vector>
#include <string>
using namespace std;

#define MOD_ADLER 65521
#define HASH_SIZE 1000 
#define BLOCK_SIZE 5

//数据类型
enum enType
{
	EN_DATA = 1,
	EN_BLOCK_NUM,
};

//hash表节点定义
typedef struct stNode
{
	unsigned int uAdlerValue;
	char aMd5Value[32];
	unsigned long long ullBlockNum;
	struct stNode *pNext;
}tCheckNode;

//hash表根节点列表
tCheckNode * gHashTable[HASH_SIZE];

//散列函数
int GetHashIndex(unsigned int iNum);
//hash表初始化
void InitHashTable();
//hash表销毁
void DestroyHashTable();
//hash表增加节点
void AddNode(tCheckNode stNode);
//从hash表中查询是否存在匹配的adler弱校验值
bool MatchAdler(tCheckNode stNode);
//从hash表中查询是否存在匹配的md5强校验值,并返回该块号,没有则返回0
unsigned long long GetMatchMd5BlockNum(tCheckNode stNode);
//计算adler32弱校验码
unsigned int adler32(unsigned char *data, size_t len);
//计算md5强校验码
void calculateMD5(unsigned char* input , unsigned int len ,  unsigned char* md5Hash);
//转16进制可见字符串
void convertToHexString(unsigned char* md5Hash, char* md5String);
//获取目标文件校验信息,存入hash表
bool GetCheckInfo(char *aFilePath);
//比对源文件和hash表的校验值,获得重组信息放入vector
bool GetRebuildInfo(char *aSrcPath , vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo);
//打印重组信息
void PrintRebuildInfo(vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo);
//重组目标文件
bool Rebuild(char *aDstFile , vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo);



int GetHashIndex(unsigned int iNum)
{
	return iNum%HASH_SIZE;
}

void InitHashTable()
{
	int i = 0;
	for(i = 0 ; i < HASH_SIZE ; i++)
	{
		gHashTable[i] = NULL;
	}
}

void DestroyHashTable()
{
	int i = 0;
	for(i = 0 ; i < HASH_SIZE ; i++)
	{
		tCheckNode *pNow = gHashTable[i];
		if(NULL != pNow)
		{
			while(1)
			{
				tCheckNode *pNext = pNow->pNext;
				free(pNow);
				pNow = pNext;
				if(NULL == pNow)
					break;
			}
		}
	}
}

void AddNode(tCheckNode stNode)
{
	int iIndex = GetHashIndex(stNode.uAdlerValue);
	tCheckNode *pRoot = NULL;
	tCheckNode *pNow = NULL;
	if(NULL == pRoot)
	{
		//创建根节点
		pRoot = (tCheckNode*)malloc(sizeof(tCheckNode));
		memcpy(pRoot , &stNode , sizeof(tCheckNode));
		gHashTable[iIndex] = pRoot;
		printf("Create root node. index[%d] , block num[%llu] , alder[%u] , md5[%s]\n" , iIndex , stNode.ullBlockNum , stNode.uAdlerValue , stNode.aMd5Value);
	}
	else
	{
		//尾部追加节点
		pNow = pRoot;
		while(1)
		{
			if(pNow->pNext)
			{
				pNow = pNow->pNext;
			}
			else
			{
				pNow->pNext = (tCheckNode*)malloc(sizeof(tCheckNode));
				memcpy(pNow->pNext , &stNode , sizeof(tCheckNode));
				printf("Add new node. index[%d] , block num[%llu] , alder[%u] , md5[%s]\n" , iIndex , stNode.ullBlockNum , stNode.uAdlerValue , stNode.aMd5Value);
				break;
			}
		}
	}
}

bool MatchAdler(tCheckNode stNode)
{
	int iIndex = GetHashIndex(stNode.uAdlerValue);
	unsigned uAdler = stNode.uAdlerValue;
	tCheckNode *pRoot = gHashTable[iIndex];
	tCheckNode *pNow = NULL;
	bool bFindFlag = false;
	do{
		if(NULL == pRoot)
		{
			break;
		}
		pNow = pRoot;
		while(1)
		{
			if(NULL == pNow) //直到链表尾都没匹配
			{
				bFindFlag = false;
				break;
			}
			else if(pNow->uAdlerValue == uAdler)
			{
				//printf("[%u]~[%u]\n" , pNow->uAdlerValue , uAdler);
				bFindFlag = true;
				break;	
			}
			else
			{
				//printf("[%u]~[%u]\n" , pNow->uAdlerValue , uAdler);
				pNow = pNow->pNext;
			}
		}

		if(bFindFlag == false)
			break;

		return true;
	}while(0);

	return false;
}

unsigned long long GetMatchMd5BlockNum(tCheckNode stNode)
{
	int iIndex = GetHashIndex(stNode.uAdlerValue);
	unsigned uAdler = stNode.uAdlerValue;
	tCheckNode *pRoot = gHashTable[iIndex];
	tCheckNode *pNow = NULL;
	bool bFindFlag = false;
	do{
		if(NULL == pRoot)
			break;
		pNow = pRoot;
		while(1)
		{
			if(NULL == pNow) //直到链表尾都没匹配
			{
				bFindFlag = false;
				break;
			}
			else if(pNow->uAdlerValue == uAdler && memcmp(stNode.aMd5Value , pNow->aMd5Value , 32) == 0)
			{
				bFindFlag = true;
				break;	
			}
			else
			{
				pNow = pNow->pNext;
			}
		}

		if(bFindFlag == false)
			break;

		return pNow->ullBlockNum;
	}while(0);

	return 0;
}

unsigned int adler32(unsigned char *data, size_t len = 700)
{
    unsigned int a = 1, b = 0;
    size_t index;
    for (index = 0; index < len; ++index)
    {
        a = (a + data[index]) % MOD_ADLER;
        b = (b + a) % MOD_ADLER;
    }
    return (b << 16) | a;
}

void calculateMD5(unsigned char* input , unsigned int len ,  unsigned char* md5Hash)
{
    MD5_CTX context;
    MD5_Init(&context);
    MD5_Update(&context, input, len);
    MD5_Final(md5Hash, &context);
}

void convertToHexString(unsigned char* md5Hash, char* md5String)
{
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
        sprintf(&md5String[i * 2], "%02x", (unsigned int)md5Hash[i]);
    }
}

bool GetCheckInfo(char *aFilePath)
{
	FILE *fp = fopen64(aFilePath , "r");
	if(NULL == fp)
	{
		puts("Func fopen64 error!");
		return false;
	}

	unsigned char alBuf[BLOCK_SIZE+1] = {0};
	int i = 0;
	while(1)
	{
		memset(alBuf , 0 , BLOCK_SIZE);
		fread(alBuf , BLOCK_SIZE , 1 , fp);
		printf("buf[%s] " , alBuf);
		i++;
		unsigned int uAdler = adler32(alBuf , BLOCK_SIZE);
		char alMd5Hex[32+1] = {0};
		unsigned char alMd5[16+1] = {0};
		calculateMD5(alBuf , BLOCK_SIZE , alMd5);
		convertToHexString(alMd5 , alMd5Hex);

		tCheckNode tNode;
		memset(&tNode , 0 , sizeof(tCheckNode));
		tNode.uAdlerValue = uAdler;
		memcpy(tNode.aMd5Value , alMd5Hex , 32);
		tNode.ullBlockNum = i;
		AddNode(tNode);

		if(feof(fp) == true)
			break;
	}

	fclose(fp);
	return true;
}

bool GetRebuildInfo(char *aSrcPath , vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo)
{
	FILE *fp = fopen64(aSrcPath , "r");
	if(NULL == fp)
	{
		puts("Func fopen64 error!");
		return false;
	}

	unsigned char alBuf[BLOCK_SIZE+1] = {0};
	bool bMatchFlag = false;
	vector<unsigned char> vecBuffer;

	int i = 1;
	while(1)
	{
		memset(alBuf , 0 , BLOCK_SIZE);
		unsigned long long ullMatchBlockNum = 0;
		size_t iReadSize = fread(alBuf , 1 , BLOCK_SIZE , fp);
		unsigned int uAdler = adler32(alBuf , BLOCK_SIZE);
		char alMd5Hex[32+1] = {0};
		unsigned char alMd5[16+1] = {0};
		calculateMD5(alBuf , BLOCK_SIZE , alMd5);
		convertToHexString(alMd5 , alMd5Hex);

		tCheckNode tNode;
		memset(&tNode , 0 , sizeof(tCheckNode));
		tNode.uAdlerValue = uAdler;
		memcpy(tNode.aMd5Value , alMd5Hex , 32);
		tNode.ullBlockNum = i++;

		//printf("Curr check value. block num[%llu] , alder[%u] , md5[%s]\n" , tNode.ullBlockNum , tNode.uAdlerValue , tNode.aMd5Value);
		//比较adler
		if(MatchAdler(tNode) == false)
		{
			bMatchFlag = false;
		}
		else
		{
			//比较md5
			ullMatchBlockNum = GetMatchMd5BlockNum(tNode);
			if(ullMatchBlockNum == 0)
			{
				bMatchFlag = false;
			}
			else
			{
				bMatchFlag = true;
			}
		}

		if(bMatchFlag == false) //未比较成功偏移一字节	
		{
			//保存需偏移的字节内容
			if(feof(fp) == true)
			{
				for(int i = 0 ; i < iReadSize ; i++)
				{
					vecBuffer.push_back(alBuf[i]);
				}
				//记录新增内容
				vecRebuildInfo.push_back(make_pair(EN_DATA , vecBuffer));
			}
			else
			{
				vecBuffer.push_back(alBuf[0]);
				fseeko64(fp , 1-iReadSize , SEEK_CUR);
			}
		}
		else
		{
			//记录新增内容
			if(vecBuffer.size() != 0)
			{
				vecRebuildInfo.push_back(make_pair(EN_DATA , vecBuffer));
			}
			vecBuffer.clear();
			//记录块号
			char alTmp[100] = {0};
			sprintf(alTmp , "%llu" , ullMatchBlockNum);
			vecBuffer = vector<unsigned char>(alTmp , alTmp+strlen(alTmp));
			vecRebuildInfo.push_back(make_pair(EN_BLOCK_NUM , vecBuffer));
			vecBuffer.clear();
		}

		//读到文件尾跳出循环
		if(feof(fp) == true)
			break;
	}
	
	fclose(fp);
	return true;
}

void PrintRebuildInfo(vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo)
{
	int i = 1;
	vector< pair<enType , vector<unsigned char> > >::iterator it = vecRebuildInfo.begin();
	for(; it != vecRebuildInfo.end() ; it++)
	{
		printf("index[%d]: " , i++);
		string sData(it->second.begin() , it->second.end());
		if(it->first == EN_DATA)
		{
			printf("data[%s]\n" , sData.c_str());
		}
		else
		{
			printf("block num[%s]\n" , sData.c_str());
		}
	}
}

bool Rebuild(char *aDstFile , vector< pair<enType , vector<unsigned char> > > &vecRebuildInfo)
{
	FILE *fp = fopen64(aDstFile , "r");
	if(NULL == fp)
	{
		puts("Func fopen64 error!");
		return false;
	}

	char alTmpFile[1024] = {0};
	sprintf(alTmpFile , "%s.tmp" , aDstFile);
	FILE *new_fp = fopen64(alTmpFile , "w");
	if(NULL == new_fp)
	{
		puts("Func fopen64 error!");
		fclose(fp);
		return false;
	}

	for(const auto &vecNode : vecRebuildInfo)
	{
		if(vecNode.first == EN_DATA)
		{
			//新增数据
			unsigned long long ullSize = vecNode.second.size();
			char *pBuf = (char *)malloc(ullSize);
			memset(pBuf , 0 , ullSize);
			copy(vecNode.second.begin(), vecNode.second.end(), pBuf);
			fwrite(pBuf , ullSize , 1 , new_fp);
			printf("Write new data[%s] to new file.\n" , pBuf);
			free(pBuf);
		}
		else
		{
			//已有数据
			string sTmp(vecNode.second.begin() , vecNode.second.end());
			int iBlockNum = atoi(sTmp.c_str());
			char alBuf[BLOCK_SIZE+1] = {0};
			//文件指针偏移
			fseeko64(fp , BLOCK_SIZE*(iBlockNum-1) , SEEK_SET);
			fread(alBuf , 1 , BLOCK_SIZE , fp);
			fwrite(alBuf , BLOCK_SIZE , 1 , new_fp);
			printf("Write blocknum[%d] data[%s] to new file.\n" , iBlockNum , alBuf);
		}
	}

	fclose(fp);
	fclose(new_fp);

	remove(aDstFile);
	puts("Remove old file.");
	rename(alTmpFile , aDstFile);
	puts("Rename temp file to dstfile.");

	return true;
}

int main(int argc, char *argv[])
{
	if(argc != 3)
	{
		puts("USAGE: ./rsync_test  [src_file_path]  [dst_file_path]");
		return -1;
	}
	char alSrcPath[1024] = {0};
	char alDstPath[1024] = {0};
	strcpy(alSrcPath , argv[1]);
	strcpy(alDstPath , argv[2]);

	if(access(alSrcPath , F_OK) != 0)
	{
		puts("Src file must exist!");
		return -2;
	}

	if(access(alDstPath , F_OK) != 0)
	{
		//直接复制
		char alCmd[1024] = {0};
		sprintf(alCmd , "cp -pf %s %s" , alSrcPath , alDstPath);
		system(alCmd);
	}
	else
	{
		struct stat slStatTmp;
		memset(&slStatTmp , 0 , sizeof(struct stat));
		stat(alSrcPath, &slStatTmp);
		time_t tSrcTime = slStatTmp.st_mtime;
		unsigned long long ullSrcSize = slStatTmp.st_size;
		stat(alDstPath, &slStatTmp);
		time_t tDstTime = slStatTmp.st_mtime;
		unsigned long long ullDstSize = slStatTmp.st_size;
		if(tSrcTime != tDstTime || ullSrcSize != ullDstSize)
		{
			InitHashTable();
			puts("------------------------Create hash from dstfile-------------------------------");
			//先获取目标文件校验信息并放入hash表
			if(GetCheckInfo(alDstPath) == false)
			{
				puts("Func GetCheckInfo error!");
				return -3;
			}
			puts("------------------------Match hash from srcfile-------------------------------");
			//按1字节偏移比对强弱校验值,并存入vector容器
			vector< pair<enType , vector<unsigned char> > > vecRebuildInfo;
			GetRebuildInfo(alSrcPath , vecRebuildInfo);
			PrintRebuildInfo(vecRebuildInfo);
			puts("------------------------Rebuid dst file-------------------------------");
			Rebuild(alDstPath , vecRebuildInfo);

			DestroyHashTable();
		}
		else
		{
			printf("Src file[%s] and dst file[%s] are the same.\n" , alSrcPath , alDstPath);
		}
	}

    return 0;
}

编译:g++ rsync_test.cpp -o rsync_test -lcrypto -std=c++11

运行结果如下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/766134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【代码随想录 | Leetcode | 第五天】链表 | 移除链表元素 | 设计链表

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来移除链表元素和设计链表的分享✨ 目录 前言203. 移除链表元素707. 设计链表总结 203. 移除链表元素 ✨题目链接点这里 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所…

Nginx 解析漏洞复现

Nginx 解析漏洞复现 一、环境搭建二、漏洞原理三、漏洞复现 一、环境搭建 如下介绍kali搭建的教程 cd ~/vulhub/nginx/nginx_parsing_vulnerability // 进入指定环境 docker-compose up -d // 启动环境docker-compose ps使用这条命令查看当前正在运行的环境 访问http://y…

生成对抗网络与优化算法(第十次组会)

生成对抗网络与优化算法(第十次组会) 生成对抗网络(Generative Adversarial Network)优化算法生成对抗网络(Generative Adversarial Network) 优化算法

126、仿真-基于51单片机16×16点阵滚动显示仿真设计(Proteus仿真+程序+配套资料等)

方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ&#xff0c;在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节…

-bash: ./est.sh: /bin/bash^M: 坏的解释器: 没有那个文件或目录

方法一&#xff1a; 方法二&#xff1a; sed -i s/\r$// xxx.sh

完整的电商平台后端API开发总结

对于开发一个Web项目来说&#xff0c;无论是电商还是其他品类的项目&#xff0c;注册与登录模块都是必不可少的&#xff1b;注册登录功能也是我们在日常生活中最长接触的&#xff0c;对于这个业务场景的需求与逻辑大概是没有什么需要详细介绍的&#xff0c;市面上常见的邮箱注册…

数仓学习---7、数据仓库设计、数据仓库环境准备

这是本人的学习过程&#xff0c;看到的同道中人祝福你们心若有所向往&#xff0c;何惧道阻且长&#xff1b; 但愿每一个人都像星星一样安详而从容的&#xff0c;不断沿着既定的目标走完自己的路程&#xff1b; 最后想说一句君子不隐其短&#xff0c;不知则问&#xff0c;不能则…

Stable Diffusion - 高清局部重绘 (Inpaint) 调整脸部和手部细节

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131775232 在 Stable Diffusion 中&#xff0c;局部重绘(Inpaint)功能是一种可以让你在图像上删除不想要的区域&#xff0c;并用周围的像素自动填…

JAVA中的Socket编程、通信协议、传输协议

JAVA中的Socket编程 一、Socket概述 Socket&#xff0c;建立起客户端和服务器之间的连接&#xff0c;实现数据的传输和交互&#xff0c;它既可以发送请求&#xff0c;也可以接受请求&#xff0c;一个Socket由一个IP地址和一个端口号唯一确定&#xff0c;利用Socket能比较方便的…

Springboot + Vue 上传Word、PDF文档并保留内部格式

因为业务需求&#xff0c;上传Word文件需要编辑&#xff0c;但如何使用Blob方式&#xff0c;在数据库里存文件&#xff0c;就会造成格式消失。所以修改思路&#xff1a;上传文件到服务器本地&#xff0c;保证数据存储的完整性。 前端 <el-upload class"upload-demo&quo…

centos7.8从卸载python2,安装python3

因为目前所有环境都是python2.7.5&#xff0c;但是项目上使用的是python3.7.5&#xff0c;迫切需要使用python3.7.5验证。安装遇到困难&#xff0c;记录一下。 首先卸载python2&#xff0c;如果不想卸载python2的可以跳过 这里卸载python2和其依赖的yum 先卸载python2.7.5 …

基于意外流行的自适应模因算法求解分布式柔性作业车间调度问题——付源代码和论文

实在是太忙了&#xff0c;终于闲下来更新一下CSDN来介绍自己的工作 《Surprisingly Popular-Based Adaptive Memetic Algorithm for Energy-Efficient Distributed Flexible Job Shop Scheduling》发表在IEEE Transactions on Cybernetics上。 原文链接-可下载 Matlab代码 IEEE…

pdf能转成ppt格式吗?这几个方法你试过了吗

作为办公人士&#xff0c;掌握不同文件格式之间的转换技能是必不可少的。每当毕业季来临&#xff0c;我都会收到许多刚刚步入职场的小伙伴们的求助&#xff0c;寻找PDF转PPT的方法。所以&#xff0c;我总结了以下三种方法&#xff0c;希望能帮助大家解决这个一直以来困扰着许多…

RabbitMQ死信交换机、TTL及延迟队列

一&#xff0c;死信交换机 1&#xff0c;什么是死信交换机 了解死信交换机之前我们先来了解一下什么是死信&#xff0c;当一个队列满足下面的三种情况的时候我们一般称为死信&#xff08;dead letter&#xff09;&#xff1a; 消费者使用basic.reject或 basic.nack声明消费失…

深入 Linux 进程

问题 进程参数 和 环境变量 对于进程意味着什么&#xff1f; 进程参数和环境变量的意义 一般情况下&#xff0c;子进程的创建是为了解决某个子问题 子进程解决问题需要父进程的 "数据输入" (进程参数 & 环境变量) 设计原则&#xff1a; 子进程启动时必然用到…

SpringCloud学习路线(1)—— 从头开始的微服务

一、服务架构的历史 现有的服务框架&#xff1a; 单体架构 概念&#xff1a; 将业务所有功能集中在一个项目中开发&#xff0c;打包部署优点&#xff1a; 架构简单&#xff0c;部署成本低缺陷&#xff1a; 耦合度高 分布式架构 概念&#xff1a; 根据业务功能对系统进行拆分&a…

前端两种实现轮播图方式

今天研究两种简单实现轮播图功能的方式。 目录 Layui实现轮播图 码云下载 提取静态文件 示例 注意 参数说明 改为轮播图 增加图片资源文件 轮播栏目修改 改为上下切换 切换事件 脚本中绑定改变事件 控制器查看 Swiper实现轮播图 下载swiper 下载到本地 加载sw…

【数据结构】_3.List接口实现类ArrayList与线性表

目录 1.List接口 1.1 List接口的集合关系网络 1.2 List的使用 2. ArrayList与顺序表 2.1 线性表 2.2 顺序表 2.3 ArrayList 2.3.1 ArrayList的集合关系网络 2.3.2 ArrayList的使用 2.3.2.1 ArrayList的构造方法 2.3.2.2 ArrayList的扩容机制逻辑示图如下&#xff1…

企业如何通过CRM提高客户转化?

现如今&#xff0c;企业想要给潜在客户留下深刻的印象&#xff0c;那么就需要一系列的措施和手段。通过CRM管理系统让潜在客户信任企业&#xff0c;更好地进行客户培育&#xff0c;从而提高潜在客户转化。下面来说说&#xff0c;企业提高潜在客户转化的7个做法。 1、永远不要让…

input模糊搜索

input模糊搜索 getList() {let dicthis.queryParameters()let val this.queryParams["userName"]if (null ! val && ! val) {dic["userName"]*val*;}listPamsArchiveSearch(dic).then(response > {this.nameinfo response.data;});},主要问…