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
一個多項式就是一個位元流,也就是由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值是正確的