循環冗余檢驗算法(CRC)與幀檢驗序列(FCS)


前言:CRC循環冗余校驗和其中的FCS幀檢驗算法的學習筆記

需要明白的一點,CRC循環冗余校驗是一種常用的檢錯方法,而FCS是添加在數據后面的用來校驗的冗余碼

后面的代碼實現純粹是個人的理解來實現的,所以自己覺得可能還是有部分BUG

什么是CRC循環冗余校驗碼

在計算機網絡中的數據鏈路層傳輸數據時候,必須采用各種差錯檢測措施,為了保證數據傳輸的可靠性,這里可以可以通過循環冗余校驗CRC來進行檢驗

其實不僅在計算機網絡中,這種方法能夠對傳輸的數據幀進行差錯檢驗,那么只要是跟數據校驗有關的都可以用到,比如逆向工程中的反調試手段,對於機器碼的改變通過這種方法也能檢驗出來

CRC循環冗余校驗碼的原理

發送端,先把數據划分為組,假定每組k位比特,在每組M后面再添加供差錯檢測用的n位冗余碼(幀檢驗序列FCS),然后構成一個幀發送出去,那么就是一共發送k+n位,如下圖所示

在數據后面添加上的冗余碼稱為幀檢驗序列 FCS (Frame Check Sequence)。

接收端,接收到k+n位之后,將接收到都除以相同的P,然后檢出余數R,如果余數R為0,則證明傳輸正確

冗余碼FCS的計算

在數據后面添加上的冗余碼稱為幀檢驗序列 FCS (Frame Check Sequence)。

1、用二進制的模2運算進行2^n乘M的運算,這相當於在M后面添加n個0

2、得到的k+n位的數除以事先選定好的長度為n+1位的除數P,得出商是Q,余數是R,余數R比除數P少1位,即R是n位。

3、將余數R作為冗余碼拼接在數據M后面,一起發送出去。

計算過程如下圖所示

1、假定要傳輸的數據為M=101001(k=6);

2、假定除數為P=1101(因為選定的是除數P是4位,所以n=3,也就是P的位數-1)

3、在M后面加n個0(n=3,所以加3個0),得到M'=101001000;

4、用M'除以P

除數P是哪來的

對於CRC標准除數,一般使用多項式(或二項式)公式表示,如下圖中除數11011(poly值為0x1b)的二項式為G(X)=X4+X3+X+1,X的指數就代表了該bit位上的數據為1,(最低位為0)。

這里特別注意一下位數問題,除數的位數為二項式最高次冪+1(4+1=5),這個很重要。

其他廣泛使用的生成多項式P(X)如下所示

CRC16 = X^16 +X^15 + X^2 + 1

CRC-32 = X^32 +X^26 + X^23 + X^22 + X^16 +X^12 + X^11 +X^10 + X^8 +X^7+ X^5 +X^4 + X^2 + X + 1

代碼實現

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

#define BYTE_LENGTH 8
#define WORD_LENGTH 16

char divBinaryArray[WORD_LENGTH] = { 0 };
char bedivedBinaryArray[WORD_LENGTH] = { 0 };

uint32_t getBinaryLength(uint32_t aByte)
{
	uint32_t numLength = 0;
	uint32_t i = 0;
	for (i = 0; i < WORD_LENGTH;)
	{
		if (aByte != 0)
		{
			numLength++;
			aByte = aByte >> 1;
		}
		else
			break;
	}
	return numLength;
}

void getBinaryArray(char* arrayBinary, uint16_t t, uint16_t index)
{
	uint16_t u = 0;
	if (t != 0)
	{
		u = t % 2;  // 9 % 2 = 1
		t /= 2;  // 
		getBinaryArray(arrayBinary, t, index - 1);
		arrayBinary[index] = u;
	}
}

int main()
{
	// 存放最終數據+冗余碼
	char* pAllocFCS = NULL;
	char* p = NULL;
	// 額外的變量
	uint8_t count = 0;
	uint8_t j;
	// 臨時指針
	char* pTmp;
	
	// 除數P, 0xD -> 1101 長度為4
	uint32_t uintP = 0xD;
	uint32_t divBinaryArrayLength = getBinaryLength(uintP);

	// 被除數M, 0x29 -> 101001 長度為6
	uint32_t uM = 0x29;
	uint32_t bedivedBinaryArrayLength = getBinaryLength(uM);

	// 字節數組binaryArray
	getBinaryArray(divBinaryArray, uintP, divBinaryArrayLength - 1);
	getBinaryArray(bedivedBinaryArray, uM, bedivedBinaryArrayLength - 1);
	
	// 將M擴大2^n次
	uM <<= (divBinaryArrayLength - 1);

	// 加上除數P的長度-1 -> uN
	// bedivedBinaryArrayLength += divBinaryArrayLength -1;
	
	// 模擬發送端 -> 構造數據+冗余碼
	printf("=====模擬發送端 -> 構造數據+冗余碼=====\n");
	pTmp = bedivedBinaryArray;
	while (1)
	{
		// ”== 3“的話寫死了,因為這里用的是CRC3 
		if (bedivedBinaryArrayLength + divBinaryArrayLength - 1 + bedivedBinaryArray - pTmp == 3)
		{
			break;
		}
		if (*(char*)pTmp != 0)
		{
			printf("計算冗余碼\n", count);
			for (j = 0;j<4;j++)
			{
				pTmp[j] = pTmp[j] ^ divBinaryArray[j];
			}
		}
		else
		{			
			printf("位移\n");
			pTmp++;
		}
	}

	pAllocFCS = (char*)malloc(bedivedBinaryArrayLength + divBinaryArrayLength);
	p = pAllocFCS;
	memset(pAllocFCS, 0, bedivedBinaryArrayLength + divBinaryArrayLength - 1);
	getBinaryArray(pAllocFCS, uM, getBinaryLength(uM)-1);
	p += bedivedBinaryArrayLength;
	for (j = 0; j<3; j++) // 3的話寫死了,因為這里用的是CRC3的時候FCS就是三位
		*p++ = *pTmp++;
	
	// 模擬接收端 -> 檢查數據的差錯
	printf("=====模擬接收端 -> 檢查數據的差錯=====\n");
	pTmp = pAllocFCS;
	while (1)
	{
		// ”== 3“的話寫死了,因為這里用的是CRC3 
		if (bedivedBinaryArrayLength + divBinaryArrayLength - 1 + pAllocFCS - pTmp == 3)
		{
			break;
		}
		if (*(char*)pTmp != 0)
		{
			printf("計算冗余碼\n", count);
			for (j = 0; j<4; j++)
			{
				pTmp[j] = pTmp[j] ^ divBinaryArray[j];
			}
		}
		else
		{
			printf("位移\n");
			pTmp++;
		}
	}
	
	
	// 釋放內存
	if (pAllocFCS != NULL)
	{
		free(pAllocFCS);
		pAllocFCS = NULL;
	}
	
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM