1. 程式人生 > >串列埠傳檔案(非終端串列埠,自己定協議下位機部分C語言實現)

串列埠傳檔案(非終端串列埠,自己定協議下位機部分C語言實現)

之前專案中有個地方要實現一個功能,就是通過非終端串列埠傳輸檔案,這裡將其作為一個小功能模組,簡單的介紹一下自己是如何實現的!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;//避免野指標

}

反正測試程式碼當初配合上位機測試的時候是沒有任何問題的,二進位制檔案、*.txt、.c等檔案格式都能成功通過串列埠傳輸過來。其實做出來的過程也是相當的簡單,這個在百度上很少看到有相關的講解,如何用C實現通過串列埠傳輸檔案!這個也是完全從想到做到實現的一個過程!發現其實真的很簡單!關於SHA1校驗這個部落格裡面原始碼有介紹!