1. 程式人生 > >crc32校驗的c語言實現

crc32校驗的c語言實現

最近在做軟體升級,需要對升級檔案進行crc校驗,就學習了crc的實現原理

crc就是一個數值,該數值用於檢驗資料的正確性,crc校驗的原理就是將需要作校驗的資料與一個數據模2相除,得到的餘數即為校驗值。

      模2相除就是在除的過程中用模2加,模2加實際上就是異或運算,就是不進行進位操作,即相同為假,不相同為真。

下面是幾種CRC校驗的生成多項式:

CRC8 = X8+X5+X4+1

CRC-CCITT = X16+X12+X5+1

CRC16=X16+X15+X2+1

CRC12=X12+X11+X3+X2+1

CRC32=X32+X26+X23+X22+X16+X12+X11+X10

+X8+X7+X5+X4+X2+X+1

一個多項式就是一個位元流,也就是由0、1組合起來的一組數

我們需要獲取的常數就是上面多項式所對應的反轉多項式(反轉多項式:在資料通訊時,資訊位元組先傳送或接受低位位元組,如重新排列則影響速度)。

下面使用查表法實現crc校驗,具體步驟如下:

(1)將上次計算出的CRC校驗碼右移一個位元組;
(2)將移出的這個位元組與新的要校驗的位元組進行XOR 運算;
(3)用運算出的值在預先生成碼錶中進行索引,獲取對應的值(稱為餘式);
(4)用獲取的值與第(1)步右移後的值進行XOR 運算;
(5)如果要校驗的資料已經處理完,則第(4)步的結果就是最終的CRC校驗碼。如果還有資料要進行處理,則再轉到第(1)步執行。

本例使用crc32校驗,使用上面crc32的生成多項式,值為0xEDB88320

首先寫一個功能函式,實現生成一個crc表,然後寫一個功能函式,用於計算crc值,最後再寫一個函式用於比較crc值,下面是全部程式碼:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>

uint32_t crc32_table[256];

int make_crc32_table()
{
	uint32_t c;
	int i = 0;
	int bit = 0;
	
	for(i = 0; i < 256; i++)
	{
		c  = (uint32_t)i;
		
		for(bit = 0; bit < 8; bit++)
		{
			if(c&1)
			{
				c = (c >> 1)^(0xEDB88320);
			}
			else
			{
				c =  c >> 1;
			}
			
		}
		crc32_table[i] = c;
	}
	

}

uint32_t make_crc(uint32_t crc, unsigned char *string, uint32_t size)
{

	while(size--)
		crc = (crc >> 8)^(crc32_table[(crc ^ *string++)&0xff]);

	return crc;
}

void compare_crc(char *filename)
{
	FILE *sp = NULL;
	uint32_t srcCrc ;
	uint32_t calcuCrc = 0xffffffff;
	unsigned char buf[1024];
	uint32_t count;
	
	if(filename == NULL)
	{
		printf("filename is null\n");
		exit(1);
	}

	sp = fopen(filename, "rb");
	if(sp == NULL)
	{
		printf("open file fail\n");
		exit(1);
	}

	fread(&srcCrc, 1, 4, sp);
	printf("In %s: src crc is 0x%x\n", __FUNCTION__, srcCrc);	


        if(sp)
        {
                while(!feof(sp))
                {
                        memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
                        calcuCrc = make_crc(calcuCrc, buf, count);
                }
        }
	printf("In %s: calcuCrc is 0x%x\n", __FUNCTION__, calcuCrc);

	fclose(sp);

	if(srcCrc == calcuCrc)
	{
		printf("In %s: the calculate crc equal the src crc in file \n", __FUNCTION__);

	}
	else
	{
		printf("In %s: the calculate crc not equal the src crc in file \n", __FUNCTION__);
	}
	
}


int main()
{
	int i;
	FILE *sp = NULL;
	FILE *dp = NULL;
	uint32_t count;
	uint32_t crc = 0xFFFFFFFF;
	unsigned char buf[1024];
	

	make_crc32_table();

	sp = fopen("/home/user/work_soft/crc_check/bak/test.txt", "rb");
	if(sp == NULL)
	{
		printf("open file error\n");
		return -1;
	}
	dp = fopen("/home/user/work_soft/crc_check/bak/testcrc.txt", "wb");
	if(dp == NULL)
	{
		printf("open file error\n");
		return -1;
	}

	if(sp)
	{
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
			count = fread(buf, 1, sizeof(buf), sp);
			crc = make_crc(crc, buf, count);		
		}
	}

	printf("In main: calculate crc is 0x%x\n", crc);
	if(dp)
	{
		fwrite(&crc, 1, 4, dp);//write the crc into the file testcrc.txt
		fseek(sp, 0, SEEK_SET);
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
			fwrite(buf, 1, count, dp);
		}
		
		fclose(sp);
		fclose(dp);
	}

	/*compare crc*/
	compare_crc("/home/user/work_soft/crc_check/bak/testcrc.txt");
	

	return 0;

}

上面程式碼是根據查表法實現的,首先生成crc表,make_crc32_table就是根據crc校驗的原理實現的,對0-255進行模2除法生成crc表;

函式make_crc是根據查表法的步驟實現的;而函式compare_crc則用來開啟一個在頭部嵌入crc值的檔案,首先將檔案頭部的crc值取出(若用於功能實現,可設計一個數據結構用於存放crc等相關值,並存放在檔案的頭部),然後再讀取檔案的餘下內容進行

crc計算,將計算出的crc值與從檔案中讀出的crc值進行比較,若相等則說明檔案內容沒有出錯。下面是執行結果:


如果要將crc嵌入到檔案頭部,比如將計算得出的crc嵌入到升級檔案的頭部,由於我們生成的crc值的儲存方式可能會因為不同的主機而不同(大端或小端)

所以在將crc值嵌入到升級檔案的頭部時,最好再加一個位元組用於說明crc值得儲存方式是大端還是小端模式,這樣接收方就可以選擇同一個儲存方式,來確定讀取的crc值是正確的