C/C++實現(xiàn)crc碼計算和校驗
算法介紹
循環(huán)冗余校驗(Cyclic Redundancy Check, CRC)是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或計算機文件等數(shù)據(jù)產(chǎn)生簡短固定位數(shù)校驗碼的一種信道編碼技術(shù),主要用來檢測或校驗數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯誤。它是利用除法及余數(shù)的原理來作錯誤偵測的。
CRC校驗計算速度快,檢錯能力強,易于用編碼器等硬件電路實現(xiàn)。從檢錯的正確率與速度、成本等方面,都比奇偶校驗等校驗方式具有優(yōu)勢。因而,CRC 成為計算機信息通信領(lǐng)域最為普遍的校驗方式。常見應(yīng)用有以太網(wǎng)/USB通信,壓縮解壓,視頻編碼,圖像存儲,磁盤讀寫等
參數(shù)模型
CRC參數(shù)模型
不知道你是否遇到過這種情況,同樣的CRC多項式,調(diào)用不同的CRC計算函數(shù),得到的結(jié)果卻不一樣,而且和手算的結(jié)果也不一樣,這就涉及到CRC的參數(shù)模型了。計算一個正確的CRC值,需要知道CRC的參數(shù)模型。
一個完整的CRC參數(shù)模型應(yīng)該包含以下信息:WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT。
NAME:參數(shù)模型名稱。
WIDTH:寬度,即生成的CRC數(shù)據(jù)位寬,如CRC-8,生成的CRC為8位
POLY:十六進制多項式,省略最高位1,如 x8 + x2 + x + 1,二進制為1 0000 0111,省略最高位1,轉(zhuǎn)換為十六進制為0x07。
INIT:CRC初始值,和WIDTH位寬一致。
REFIN:true或false,在進行計算之前,原始數(shù)據(jù)是否翻轉(zhuǎn),如原始數(shù)據(jù):0x34 = 0011 0100,如果REFIN為true,進行翻轉(zhuǎn)之后為0010 1100 = 0x2c
REFOUT:true或false,運算完成之后,得到的CRC值是否進行翻轉(zhuǎn),如計算得到的CRC值:0x97 = 1001 0111,如果REFOUT為true,進行翻轉(zhuǎn)之后為11101001 = 0xE9。
XOROUT:計算結(jié)果與此參數(shù)進行異或運算后得到最終的CRC值,和WIDTH位寬一致。
接收端的校驗有兩種方式:
- 一種是和CRC計算一樣,在本地把接收到的數(shù)據(jù)和CRC分離,然后在本地對數(shù)據(jù)進行CRC運算,得到的CRC值和接收到的CRC進行比較,如果一致,說明數(shù)據(jù)接收正確,如果不一致,說明數(shù)據(jù)有錯誤。
- 另一種方法是把整個數(shù)據(jù)幀進行CRC運算,因為是數(shù)據(jù)幀相當于把原始數(shù)據(jù)左移8位,然后加上余數(shù),如果直接對整個數(shù)據(jù)幀進行CRC運算(除以多項式),那么余數(shù)應(yīng)該為0,如果不為0說明數(shù)據(jù)出錯
代碼計算
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//求數(shù)的二進制最高位的冪指數(shù),即MSB
static int getMinPolynomialBits(uint64_t n) {
int r = 0;
while (n >>= 1) r++;
return r;
}
//append>0表示計算crc校驗碼,賦值到crcRemainder
//append=0,表示校驗輸入bit流是否正確;0表示正確,-1表示錯誤
//此處的多項式默認為0x96(高位補1后的結(jié)果),默認crc位數(shù)為7,可根據(jù)代碼自行修改
static int crcCheck(const char* msg, int append, char* crcRemainder)
{
if (msg == NULL || crcRemainder == NULL || strlen(msg) == 0) {
printf("input parameter is unvalid!\n");
return -1;
}
//hex: 0x96 = b'10010110' = DEC:150
uint64_t poly = 0x96;
int polyLen = getMinPolynomialBits(poly + 1); //=7
int msgLen = strlen(msg);
//printf("%d\n", msgLen);
//計算crc校驗碼
if (append) {
unsigned char* pBufCrc = (unsigned char*)calloc(msgLen + polyLen, sizeof(unsigned char));
memset(pBufCrc, 0, msgLen + polyLen);
for (int j = 0; j < msgLen; j++) {
pBufCrc[j] = msg[j] - '0';
}
uint8_t* p = NULL;
for (int i = 0; i < msgLen; i++) {
if (pBufCrc[i]) {
p = pBufCrc + i + polyLen;
uint64_t t = poly;
do {
*(p--) ^= t & 1;
} while (t >>= 1);
}
}
p = NULL;
size_t k;
for (k = 0; k < polyLen; k++) {
crcRemainder[k] = pBufCrc[k + msgLen] + 48;
}
if (pBufCrc) {
free(pBufCrc);
pBufCrc = NULL;
}
}
else {
// 校驗接受端的比特流
unsigned char* pBuffer = (unsigned char*)calloc(msgLen, sizeof(unsigned char));
memset(pBuffer, 0, msgLen);
int inforLen = msgLen - polyLen;//提取出信息流部分,然后計算當前信息對應(yīng)crc校驗碼
for (int j = 0; j < inforLen; j++) {
pBuffer[j] = msg[j] - '0';
}
uint8_t* p = NULL;
for (int i = 0; i < inforLen; i++) {
if (pBuffer[i]) {
p = pBuffer + i + polyLen;
uint64_t t = poly;
do {
*(p--) ^= t & 1;
} while (t >>= 1);
}
}
p = NULL;
//計算得到的crc碼和輸入的crc碼進行對比驗證,若每一位都相同,則校驗成功
for (size_t k = inforLen; k < msgLen; k++) {
if (msg[k] != pBuffer[k] + 48) {
if (pBuffer) {
free(pBuffer);
pBuffer = NULL;
}
return -1;
}
}
if (pBuffer) {
free(pBuffer);
pBuffer = NULL;
}
}
return 0;
}到此這篇關(guān)于C/C++實現(xiàn)crc碼計算和校驗的文章就介紹到這了,更多相關(guān)C++ crc碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用C語言winform編寫滲透測試工具實現(xiàn)SQL注入功能
本篇文章主要介紹使用C#winform編寫滲透測試工具,實現(xiàn)SQL注入的功能。使用python編寫SQL注入腳本,基于get顯錯注入的方式進行數(shù)據(jù)庫的識別、獲取表名、獲取字段名,最終獲取用戶名和密碼;使用C#winform編寫windows客戶端軟件調(diào)用.py腳本,實現(xiàn)用戶名和密碼的獲取2021-08-08
Visual Studio Community 2022(VS2022)安裝圖文方法
這篇文章主要介紹了Visual Studio Community 2022(VS2022)安裝方法,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
C語言中g(shù)etchar()與putchar()函數(shù)詳解
本文主要介紹了C語言中g(shù)etchar()與putchar()函數(shù)詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01
C++實現(xiàn)LeetCode(57.插入?yún)^(qū)間)
這篇文章主要介紹了C++實現(xiàn)LeetCode(57.插入?yún)^(qū)間),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07

