crc32 演算法與實現
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
- #include
- #include
- #include
- 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("/home/user/work_soft/crc_check/bak/testcrc.txt");
- return 0;
- }
函式make_crc是根據查表法的步驟實現的;而函式compare_crc則用來開啟一個在頭部嵌入crc值的檔案,首先將檔案頭部的crc值取出(若用於功能實現,可設計一個數據結構用於存放crc等相關值,並存放在檔案的頭部),然後再讀取檔案的餘下內容進行
crc計算,將計算出的crc值與從檔案中讀出的crc值進行比較,若相等則說明檔案內容沒有出錯。下面是執行結果:
如果要將crc嵌入到檔案頭部,比如將計算得出的crc嵌入到升級檔案的頭部,由於我們生成的crc值的儲存方式可能會因為不同的主機而不同(大端或小端)
所以在將crc值嵌入到升級檔案的頭部時,最好再加一個位元組用於說明crc值得儲存方式是大端還是小端模式,這樣接收方就可以選擇同一個儲存方式,來確定讀取的crc值是正確的