之前项目中有个地方要实现一个功能,就是通过非终端串口传输文件,这里将其作为一个小功能模块,简单的介绍一下自己是如何实现的!SecureCRT超级终端上可以通过lrz等工具通过命令传输文件这个就不介绍,之前的博客也有写过怎么编译移植使用这个工具,这次是自己来编码实现通过非终端调试串口来安全可靠的传输任何文件。
其实刚开始想的时候觉得有一点难度,一是要自己定协议,如何定协议才合理呢?二是当时自己也想的有点复杂,一个文件所包含的文件信息又有哪些呢?通过串口传输过来的数据又需要怎样的及时的写入文件呢?传输文件又用到什么校验算法呢?反正一开始自己脑子里就是一大堆的问题!想出问题来了就好说了!既然是C语言,还是自己定协议,那当然就是一步一步的来了!
我们这个要结合上位机软件,上位机软件是别人写的!憋屈自己不会上位机,文件校验用的是SHA1校验算法,博客里面也有!
首先项目需求是我们要传输的文件大小不会很大,大到天也不会超过5M,协议我自己是这样定的:
------------------------------------------------------------------------------------------------------------------------
10字节,传输的字节总长度 | 255字节 文件名 | 20字节的SHA1校验码(直接)
-------------------------------------------------------------------------------------------------------------------------
对于一个文件的关键信息,就是文件名,文件数据长度!有了这两个关键信息,这样就可以在Linux下通过C应用编程实现还原传输过来的文件!
整个过程分为两次数据包传送:
第一个数据包为固定长度 285字节,传输的字节数还有文件名上位机都是以字符串的形式(也就是ASC形式)传输过来的,后面20字节是直接传输,不用转化!字节数和文件名长度不够的地方都是以空格填充!
第二个数据包是直接传输文件里面的数据,把文件里面的数据原封不动的传输过来,同时下位机这边接收数据,malloc出一片内存来暂时存放临时数据,文件数据传输完毕后,计算20字节SHA1校验码,把生成的校验码与上位机传输过来的校验码对比,如果完全一致,表示传输成功!
串口read数据是用非阻塞的方式!这里第一个数据包传输完后会向上位机返回一个确认信号!这个也是完全自定义!
这里简单的贴上下位机部分处理的代码!上位机COM测试程序我这里没有源码,只有一个应用程序,传上来也没什么大用!当然这里处理部分的代码只能作为参考!前期写的还是比较粗糙!(这个是测试部分的代码)
static void chmodfile777(void)//改变传输过来文件的权限 这里调用一个shell脚本
{
int status = 0;
status = system("./Appupdate.sh");
if(-1 == status)
{
printf("system error!");
}
else
{
printf("exit status value = [0x%x]\n", status);
}
}
struct file_inode
{
int filesize;
char filename[256];
char shal[21];
}fileinfo;
------------------------------------------------------------------
10字节,传输的字节总长度| 255字节 文件名 | 20字节的SHA1校验码
------------------------------------------------------------------
void Cmd_Func_IAP(char (*p)[ONEMSGLENTH])
{
int len;
int wlen;
static int nreadsize = 0;
int itemfd = -1; //文件fd
char temp[10] = {'\0'};
char tmp0[300] = {'\0'};
char str[25] = {'\0'};
SHA1Context sha;
int i, err;
uint8_t Message_Digest[20];
char *Pstrcache = NULL;
int equal = 0;
int fnamesize = 0;
nreadsize = 0;
while(nreadsize < 285)
{
//第一步 上位机先发送一个数据包 接收成功 下位机返回一个信号 EA 数据包包括4字节文件长度 255字节文件名 20字节校验码 包括 ( 报文长度 10 - 255 - 20 )
len = read(fd, temp, 8);
//printf("Alen = %d\n", len);
if(len > 0)
nreadsize += len;
temp[len] = '\0';
strcat(tmp0,temp);
bzero(temp,10);
//printf("nreadsize = %d\n", nreadsize);
}
//printf("aaaaaaaaaaaaaaaaaaaaaaa\n");
//printf("readsizelen = %d\n", nreadsize);
tmp0[285] = '\0';
//开始解析数据
strncpy(str,tmp0,10);
str[10] = '\0';
fileinfo.filesize = atoi(str);
strncpy(&(fileinfo.filename[0]), &(tmp0[10]), 255);
strncpy(&(fileinfo.shal[0]), &(tmp0[265]), 20);
fileinfo.filename[255] = '\0';
//printf("fileinfo.filesize = %d\n", fileinfo.filesize);
//printf("fileinfo.filename = %s\n", fileinfo.filename);
//printf("\nfileinfo.shal = ");
while(fileinfo.filename[fnamesize] != 32) //文件名多余的用空格填充 空格就是32
{
fnamesize++;
}
memset(tmp0, '\0' ,300);
strncpy(tmp0,"/opt/START/", strlen("/opt/START/"));
tmp0[strlen("/opt/START/")] = '\0';
fileinfo.filename[fnamesize] = '\0';
strcat(tmp0, fileinfo.filename);
itemfd = open(tmp0, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if(itemfd != -1) //如果成功 开始传输文件数据
{
fileinfo.filesize = fileinfo.filesize - 285;
Pstrcache = (char *)malloc( sizeof(char)*(fileinfo.filesize) );
nreadsize = 0;
len = 0;
if(Pstrcache != NULL)
{
while(nreadsize < fileinfo.filesize )
{
len = read(fd, &(Pstrcache[nreadsize]),fileinfo.filesize);
if(len > 0)
nreadsize += len;
//printf("nreadsize = %d\n", nreadsize);
len = 0;
}
}
//校验 检查校验 写入文件
err = SHA1Reset(&sha);
err = SHA1Input(&sha,(const unsigned char *) Pstrcache,fileinfo.filesize);
err = SHA1Result(&sha, Message_Digest);
for(i = 0; i < 20 ; ++i)
{
//printf("%02X ", Message_Digest[i]);
if( Message_Digest[i] != fileinfo.shal[i] ) //比较校验码 如果不相同表示接受文件失败
{
printf("recevice fail!\n");
len = write(fd, "update fail!\n",sizeof("update fail!\n") );
equal = 1;
//break;
}
}
if(equal != 1)//表示传输的文件完全正确
{
wlen = write(itemfd, Pstrcache, fileinfo.filesize);
len = write(fd, "update succeed!\n",sizeof("update succeed!\n") );
chmodfile777();//通过脚本 将下载后的权限改为可执行
}
}
free(Pstrcache);
Pstrcache = NULL;//避免野指针
}