需求背景:
将嵌入式系统的系统文件下载到下位机的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;
}