用TCP协议传输大文件(使用了md5校验保证传输的正确性)

需求背景:

将嵌入式系统的系统文件下载到下位机的sd卡中并替换旧的系统文件;达到系统升级的目标;

开发环境:

上位机发送端使用windows系统,Labwindow/CVI软件;

下位机接收端使用linux16.04系统,Zynq提供的SDK开发环境;

上位机发送端代码:

//声明套接字缓冲区和一次发送文件数据的缓冲区大小
#define SOCKET_BUFF 80000                 //套接字缓冲区大小
#define PACK_BUFF 50000                   //数据包缓冲区大小

//声明文件I/O缓冲区和最大文件路径长度
#define FILE_NAME_MAX 100                 //文件路径最大长度
#define FILE_IO_BUFF PACK_BUFF            //文件IO缓冲区  

//接收端接收到发送端发送的数据后会告诉发送端,发送端接收到该指令后,将该标志位置1,表示可以继续发
//送下一包数据
int sendFinishFlag = 1;

//文件信息
typedef struct _FileInfor    
{
    unsigned long long int ulFileLen;
    char sFileName[FILE_NAME_MAX];
}_FileInfor;

//数据包
typedef struct _DataPack
{
    char cType;        					    //'D'为数据  'M'为文件信息
    unsigned int nPackLen;                  //数据包大小
	size_t nPosition;                	    //数据在文件中的位置
	size_t nContentLen;                	    //数据字节数  
	_FileInfor	FileInfor;        		    //文件信息
	unsigned char decrypt[16];			    //用md5加密方式加密的加密码
	unsigned char sContent[PACK_BUFF];      //数据包缓冲区 
}_DataPack;

//从文件路径中提取文件名字
static void GetFileNameFromPath(char *filePathName, char *fileName)
{
	char temp[MAX_PATHNAME_LEN]="../systemFileBackups/"; //需要升级下载的文件路径
	int j = 0;
	while(temp[j] != '\0')
	{
		j++;
	}
	int k = 0;
	while(filePathName[j] != '\0')
	{
		fileName[k] = filePathName[j];
		j++;
		k++;
	}	
}

//发送主程序
static int updateSystem()
{
			ssize_t filesize;
			FILE *fp;
			unsigned char send_buf[80]={0};
			char temp[MAX_PATHNAME_LEN]="../systemFileBackups/"; //需要升级下载的文件路径
			char path[MAX_PATHNAME_LEN]="../systemFileBackups/*.*";
			char fileName[MAX_PATHNAME_LEN];
			char tempFileName[MAX_PATHNAME_LEN];
			char fileNameArray[4][MAX_PATHNAME_LEN]={0};
			unsigned char fileNum = 0;
			
			//获取所有待下载的系统文件存放到文件数组里
			GetFirstFile (path,  1, 1, 1, 1, 1, 0, fileName);
			sprintf(tempFileName, "%s%s", temp, fileName);
			strcpy(fileNameArray[fileNum], tempFileName);
			fileNum++;
			while(GetNextFile(fileName)==0)	
			{
				sprintf(tempFileName, "%s%s", temp, fileName);
				strcpy(fileNameArray[fileNum], tempFileName);  
				fileNum++;
			}
			
			//遍历打包发送文件名和文件大小给到服务器
			for(unsigned char i = 0;i < fileNum;i++)
			{
				GetFileSize (fileNameArray[i], &filesize);
				//发送升级指令
				send_buf[0] = 0xaa;
				send_buf[1] = fileNum;							//文件个数
				send_buf[2] = 0x18;
				send_buf[3] = filesize%10000000000/1000000000;	//filesize最大支持1*10e;
				send_buf[4] = filesize%1000000000/100000000;
				send_buf[5] = filesize%100000000/10000000;
				send_buf[6] = filesize%10000000/1000000;
				send_buf[7] = filesize%1000000/100000;
				send_buf[8] = filesize%100000/10000;
				send_buf[9] = filesize%10000/1000;
				send_buf[10] = filesize%1000/100;
				send_buf[11] = filesize%100/10;
				send_buf[12] = filesize%10;
				send_buf[13] = i;		  						//文件序列号
				int j = 14, k=21;
				while(fileNameArray[i][k] != '\0')
				{
					send_buf[j] = fileNameArray[i][k];
					j++;k++;
				}
				send_buf[79] = GetXorCheckVal(send_buf,79);
				ClientTCPWrite (g_hconversation, (char*)send_buf, 80, 1000);
				ZeroMemory(&send_buf[14], 20);
			}
			
			//切换网络发送文件数据
			Delay(1);
			InetFTPClose(ftp_handle); 
			if(ClientInit1()<0)//判断网络是否可用。初始化网络2(退出时关闭)可以初始化中断函数接收下位机的升级成功命令,连接服务器 
			{
				MessagePopup("TCP Clicd", "Connection to server failed !"); //提示连接失败
				return -2; 
			}
			else
			{
				//printf("进入发送文件\n");
				//遍历打包发送文件数据
				sendFinishFlag = 1;    
				for(unsigned char i = 0;i < fileNum;i++)
				{
					GetFileSize (fileNameArray[i], &filesize);
					//打开文件
					fp=fopen(fileNameArray[i],"rb");    
					if(fp==NULL)	
					{
						MessagePopup ("Warning", "发送文件数据时,打开文件失败!");
						return -1;
					}
					int nBuff = 100000;
					if(setvbuf(fp, (char*)&nBuff, _IOFBF, sizeof(nBuff)))
						MessagePopup("Warning", "设置文件缓冲区失败!");
					//读取文件数据并发送
					size_t ulFlagCount = 0; //记录读了多少数据
					size_t FaceReadByte = 0; //一次实际读取的字节数
					char sBuff[PACK_BUFF];
				   	ZeroMemory(sBuff, PACK_BUFF);
					fseek(fp, 0, SEEK_SET);
					while(!feof(fp))
					{
						if(sendFinishFlag == 1)
						{	
							FaceReadByte = fread(sBuff, 1, PACK_BUFF, fp);
							//打包
							_DataPack DataPack;
							_DataPack revDataPack;
							DataPack.cType = 'D';
							DataPack.nPackLen = sizeof(DataPack);
							DataPack.nContentLen = FaceReadByte;
							CopyMemory(DataPack.sContent, sBuff, FaceReadByte);
							//	strncpy(DataPack.sContent, sBuff, FaceReadByte);
							DataPack.nPosition = ulFlagCount;
							GetFileNameFromPath(fileNameArray[i],              DataPack.FileInfor.sFileName);
							DataPack.FileInfor.ulFileLen =  filesize;
							//MD5加密
							MD5_CTX md5;
							MD5Init(&md5);
							MD5Update(&md5, DataPack.sContent, DataPack.nContentLen);
							MD5Final(&md5, DataPack.decrypt);  
							//printf("加密码***");
							//for(int k=0;k<16;k++)
							//{
							//	printf("%02x ",DataPack.decrypt[k]);
							//}
							//printf("***加密码\n");
							//
							//printf("DataPack.nPackLen:%d\n",DataPack.nPackLen);
							//printf("DataPack.nPosition:%lld\n",DataPack.nPosition);
							//printf("DataPack.nContentLen:%lld\n",DataPack.nContentLen);
							//printf("DataPack.FileInfor.sFileName:%s   DataPack.FileInfor.ulFileLen:%lld\n",DataPack.FileInfor.sFileName, DataPack.FileInfor.ulFileLen);
						
							int bytesWritten  = ClientTCPWrite (g_hconversation1, (char*)&DataPack, DataPack.nPackLen, 1000);	//发送文件
						    //printf("bytesWritten:%d\n", bytesWritten);
							sendFinishFlag = 0;
							int n;
							while((n = ClientTCPRead (g_hconversation1, &revDataPack, sizeof(revDataPack), 5000)) > 0)
							{
								if('A' == revDataPack.cType)
								{
									sendFinishFlag = 1;
								}
							}
							char fileNameRcv[50];
							sprintf(fileNameRcv, "%s%s", "./", DataPack.FileInfor.sFileName);
							FILE *fpRcv = fopen("fileNameRcv","ab");
							if(fpRcv == NULL)	MessagePopup("Warning", "接收文件时打开失败");
							//size_t nPosition = DataPack.nPosition;
							//int nRe = fseek(fpRcv, nPosition, SEEK_SET);
							//printf("准备要写的位置:DataPack.nContentLen:%lld\n", DataPack.nContentLen);
							size_t rNumberOfBytesWrite = fwrite(DataPack.sContent,1,DataPack.nContentLen,fpRcv);
							//if(rNumberOfBytesWrite == DataPack.nContentLen)	
							//{
							//	printf("写成功\n");
							//}
							fflush(fpRcv);
							fclose(fpRcv);
							
							memset(&DataPack, 0, sizeof(DataPack));
							memset(sBuff, 0, PACK_BUFF);
							ulFlagCount += FaceReadByte;
						}
						Sleep(1);
					}
					//关闭文件
					fclose(fp);
				}
				DisconnectFromTCPServer(g_hconversation1);		  						//断开与TCP服务器的连接 
				InetFTPClose(ftp_handle); 
			}
			Delay(5);
			unsigned char measRxBuf[20] = {0};
			ssize_t rxNum = sizeof (measRxBuf);
			rxNum = ClientTCPRead (g_hconversation, measRxBuf, rxNum, 5000);					//读服务器 ,判断下位机是否发来升级成功指令
			if((measRxBuf[2]==0x01)&&(measRxBuf[1]==0x16)&&(measRxBuf[19]==GetXorCheckVal(measRxBuf,19)))
			{
				MessagePopup("Message", "Update Successfully !!!"); 
			}
			else
			{
				MessagePopup("Message", "Update fail !!!"); 
				return -3; 
			}
			return 0;
}

下位机接收端代码:

typedef struct _FileInfor
{
	uint64_t ulFileLen;
	char sFileName[FILE_NAME_MAX];
}_FileInfor;

typedef struct _DataPack
{
	char cType;
	unsigned int nPackLen;
	uint64_t nPosition;
	uint64_t nContentLen;
	_FileInfor FileInfor;
	unsigned char md5[16];
	unsigned char sContent[PACK_BUFF];
}_DataPack;
_FileInfor fileNmaeArray[4];

/*
 * function:Update system file contains:
 * autostart.sh
 * BOOT.BIN
 * deviceIP.sh
 * image.ub
 */
int updateSystem(int listenfd,uint8_t *recvBuff,int connfd1)
{
	int fileNum = 0;
	int bytesRecv = 0;
	int connfd2=0;
	unsigned int FileSize = 0;
	char fileName[50] = {0};
	uint8_t sendBuff[20] = {0};
    fileNum =recvBuff[1];
    LOG("文件数量:%d\n", fileNum);
	FileSize = recvBuff[3]*1000000000+recvBuff[4]*100000000+recvBuff[5]*10000000+recvBuff[6]*1000000+recvBuff[7]*100000+recvBuff[8]*10000+recvBuff[9]*1000+recvBuff[10]*100+recvBuff[11]*10+recvBuff[12];//Calculate file size for transfer
	for(int i =0;i < 40;i++)	fileName[i] = recvBuff[14+i];
	LOG("第%d个文件:%s:————————File Size——————————%d\n",recvBuff[13], fileName, FileSize);
	fileNmaeArray[recvBuff[13]].ulFileLen = FileSize;
	strcpy(fileNmaeArray[recvBuff[13]].sFileName, fileName);
	LOG("fileNmaeArray[%d].ulFileLen:%lld\n", recvBuff[13], fileNmaeArray[recvBuff[13]].ulFileLen);
	LOG("fileNmaeArray[%d].sFileName:%s\n", recvBuff[13], fileNmaeArray[recvBuff[13]].sFileName);

	char path[10] = "/mnt/";
	char filePath[FILE_NAME_MAX];
	uint64_t ulWriteByteCount = 0;
	uint64_t ulWriteByteTotal = 0;
	if(recvBuff[13] == (fileNum-1))
	{
		char BufferRecv[PACK_BUFF+5000];
		_DataPack DataPack;
		_DataPack ACKDataPack;
		int totalBytesRecv;
		memset(&DataPack, 0, sizeof(DataPack));
		memset(&ACKDataPack, 0, sizeof(ACKDataPack));
		connfd2 = accept(listenfd, (struct sockaddr*)NULL, NULL);
		while ((bytesRecv = recv(connfd2, BufferRecv+totalBytesRecv, sizeof(DataPack), 0)) > 0)
		{
			LOG("读的字节数bytesRecv:%d\n",bytesRecv);
			totalBytesRecv += bytesRecv;
			LOG("totalBytesRecv:%d\n",totalBytesRecv);
			//if('D' == DataPack.cType && totalBytesRecv == sizeof(DataPack))
			if(totalBytesRecv == sizeof(DataPack))
			{
				memset(&DataPack, 0, sizeof(DataPack));
				memcpy(&DataPack, BufferRecv, sizeof(DataPack));
				totalBytesRecv = 0;
				LOG("DataPack.nPackLen:%d\n", DataPack.nPackLen);
				LOG("DataPack.nPosition:%lld\n",DataPack.nPosition);
				LOG("DataPack.nContentLen:%lld\n", DataPack.nContentLen);
				LOG("DataPack.FileInfor.sFileName:%s\n", DataPack.FileInfor.sFileName);
				LOG("DataPack.FileInfor.ulFileLen:%lld\n",DataPack.FileInfor.ulFileLen);
				LOG("加密码×××");
				for(int k = 0;k<16;k++)
					LOG("%02x ", (uint32_t)DataPack.md5[k]);
				LOG("×××加密码\n");
				//打开文件
				sprintf(filePath, "%s%s", path, DataPack.FileInfor.sFileName);
				FILE *fp = fopen(filePath, "ab");
				if(fp == NULL)	LOG("在写文件的时候打开文件失败!\n");
				else LOG("在写文件的时候打开文件成功!\n");
				//设置文件缓存区
				int nBuff = 100000;
				setvbuf(fp, (char*)&nBuff, _IOFBF, sizeof(nBuff));
				//定位文件
				LOG("定位文件\n");
				unsigned char nPosition = DataPack.nPosition;
				int nRe = fseek(fp, nPosition, SEEK_SET);
				if(nRe)	LOG("在写文件的时候定位文件失败!\n");
				//写文件
				LOG("写文件\n");
				//uint64_t rNumberOfBytesWritten;
				uint64_t rNumberOfBytesWritten = fwrite(DataPack.sContent, 1, DataPack.nContentLen, fp);
				//md5校验判断
				LOG("md5校验判断\n");
				unsigned char md5crypt[16] = {0};
				MD5_CTX fimware_md5;
				MD5Init(&fimware_md5);
				MD5Update(&fimware_md5, DataPack.sContent, DataPack.nContentLen);
				MD5Final(&fimware_md5, md5crypt);
				LOG("接收到的内容加密码×××");
				for(int k = 0;k<16;k++)
					LOG("%02x ", md5crypt[k]);
				LOG("×××加密码\n");
				//if(rNumberOfBytesWritten != DataPack->nContentLen || (strcmp(md5crypt, DataPack.md5) != 0))	LOG("写文件失败!\n");
				if(rNumberOfBytesWritten != DataPack.nContentLen)	LOG("写文件失败!\n");
				else
				{
					ulWriteByteCount += rNumberOfBytesWritten;
					//写文件成功后给发送端返回分片数据序列确认接收成功
					ACKDataPack.cType = 'A';
					ACKDataPack.nPackLen = sizeof(ACKDataPack);
					send(connfd2, (char*)&ACKDataPack, ACKDataPack.nPackLen, 0);
					LOG("写入成功,返回确认\n");
				}
				LOG("2ulWriteByteCount:%lld\n", ulWriteByteCount);
				fflush(fp);
				//关闭文件
				memset(&DataPack, 0, sizeof(DataPack));
				nRe = fclose(fp);
				if(nRe) 	LOG("关闭文件失败!");
			}
		}
		for(int j=0;j<fileNum;j++)
		{
			ulWriteByteTotal += fileNmaeArray[j].ulFileLen;
		}
		LOG("ulWriteByteTotal:%lld\n", ulWriteByteTotal);
		LOG("ulWriteByteCount:%lld\n", ulWriteByteCount);
		char filePathName1[FILE_NAME_MAX] = "cp /mnt/";
		char filePathName3[FILE_NAME_MAX] = " /media/card0";
		char filePathName4[FILE_NAME_MAX] = "rm /mnt/";

		if(ulWriteByteCount == ulWriteByteTotal)
		{
			LOG("文件下载成功\n");
			//复制文件到sd卡
			LOG("copy file to SD\n");
			for(int i = 0;i<fileNum;i++)
			{
				sprintf(filePath, "%s%s%s", filePathName1, fileNmaeArray[i].sFileName, filePathName3);
				system(filePath);
				sprintf(filePath, "%s%s", filePathName4, fileNmaeArray[i].sFileName);
				system(filePath);
			}
			 sendBuff[0] =0xaa;
			 sendBuff[1] =0x16;
			 sendBuff[2] =0x01;									//0x00--->Failed,0x01--->success
			 sendBuff[19] = GetXorCheckVal(sendBuff,19);
			 LOG("sendBuff[19]:%x\n", sendBuff[19]);
			 send(connfd1, sendBuff, 20, 0);
			 return 1;
		}
		else
		{
			LOG("文件下载失败\n");
			for(int i = 0;i<fileNum;i++)
			{
				sprintf(filePath, "%s%s", filePathName4, fileNmaeArray[i].sFileName);
				system(filePath);
			}
			sendBuff[0] =0xaa;
			sendBuff[1] =0x16;
			sendBuff[2] =0x00;									//0x00--->Failed,0x01--->success
			sendBuff[19] = GetXorCheckVal(sendBuff,19);
			LOG("sendBuff[19]:%x\n", sendBuff[19]);
			send(connfd1, sendBuff, 20, 0);
			return 0;
		}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值