CRC碼計算及原理(FCS幀校驗序列生成)

我們知道在以太網幀的末尾有一個叫FCS的東西。
全稱:Frame Check Sequence,中文名:幀檢驗序列
這個東西是用來檢驗我們的數據是否在傳輸的過程中被破壞(不一定是收到攻擊,也可能是一些物理干擾),以更好的安排重發。
而其中最常用的,也是檢錯能力很強的,就是CRC,循環冗餘校驗碼。

操作流程

一個小背景知識

模二除法,或者說在數域{1,0}上的除法。
與普通除法類似,它也可以列豎式計算,但是唯一不同的,是相減的那一步。
我們這裏的除法,在相減時,遵循一下規則:

  1. 1-1=0
  2. 1-0=1
  3. 0-1=1
  4. 0-0=0
  5. 不進位,也不借位

舉個例子。
模2除法
我相信應該很清楚了。(手畫的,不容易)

生成

首先,我們需要一個除數,這個除數可以按照某個行業標準來,比如:

IBM的SDLC(同步數據鏈路控制)規程中使用的CRC-16爲:11000000000000101,在ISO HDLC(高級數據鏈路控制)規程、ITU的SDLC、X.25、V.34、V.41、V.42等中使用CCITT-16爲:11000000000100001。

當然這裏做個實驗,我們也可以隨機生成,但是有一點要求,最高位和最低爲必須爲1,這一點需要注意。

接下來,我們把數據左移(k-1)位,補零,這就是我們的被除數。
然後用這個被除數和除數做模二除法得到餘數。
這個餘數(k-1位,不足的話,左側補零),就是加在數據末尾的FCS。
然後我們把它加在數據末尾即可。

校驗

那麼我們如何校驗呢?
直接把我們得到的串(包含FCS),對除數再做一次模二除法,如果餘數是零,則說明數據完好。如果不爲0,則說明數據遭到破壞,需要安排重發。

代碼實現

相信看了上面的內容,應該很容易實現代碼,下面給出我寫的代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
using namespace std;
struct bin{//二進制數據 
	int num[100000];
	int len;
	void init(){//初始化爲0 
		len=1;
		memset(num,0,sizeof(num));
	}
	void read(){//讀入數字 
		char c[100000];
		scanf("%s",c);
		len=strlen(c);
		for(int i=0;i<len;i++){
			num[i]=c[len-i-1]-'0';
		}
	}
	void clean(){//清除前導0 
		for(int i=99999;i>=0;i--){
			if(num[i]==1){
				len=i+1;
				return;
			}
		}
		len=1;
	}
	void gen(int k){//隨機生成 
		len=k;
		num[len-1]=num[0]=1;
		for(int i=1;i<len-1;i++){
			num[i]=rand()%2;
		}
	}
	bin operator =(bin b){//賦值符號 
		len=b.len;
		for(int i=0;i<len;i++){
			num[i]=b.num[i];
		}
		return *this;
	}
	bin operator +(bin b){//加法 
		bin c;
		c.init();
		c.len=max(b.len,len);
		for(int i=0;i<c.len;i++){
			c.num[i]=num[i]+b.num[i];
		}
		return c;
	}
	bin operator <<=(int b){//左移 
		for(int i=len-1;i>=0;i--){
			num[i+b]=num[i];
			num[i]=0;
		}
		len+=b;
		return *this;
	}
	bin operator /(bin b){//除法取餘數(返回值爲餘數) 
		bin c;
		c.init();
		c=*this;
		for(int i=c.len-1;i>=0&&i>=(b.len-1);i--){
			if(!c.num[i]){
				continue;
			}
			for(int j=i;j>i-b.len;j--){
				c.num[j]-=b.num[j-i+b.len-1];
				if(c.num[j]<0){
					c.num[j]=1;
				}
			}
		}
		c.clean();
		return c;
	}
	int operator ==(bin b){//等於號 
		if(b.len!=len){
			return 0;
		}
		for(int i=0;i<len;i++){
			if(num[i]!=b.num[i]){
				return 0;
			}
		}
		return 1;
	}
	void print(){//打印 
		for(int i=len-1;i>=0;i--){
			printf("%d",num[i]);
		}
		printf("\n");
	}
};
int main(){
	//設定k值 
	int k=5;
	srand(time(0));
	//定義各種量 
	bin text,key,div,txt,zero;
	//初始化 
	div.init();
	key.init();
	txt.init();
	zero.init();
	text.init();
	//生成 
	text.read();//讀入數據 
	key.gen(5);//生成除數 
	txt=text; 
	txt<<=(k-1);//數據左移 
	div=txt/key;//取餘數 (FCS) 
	txt=txt+div;// 合成 
	//模擬破壞  
//	txt.num[txt.len/2]^=1;
	//校驗
	txt=txt/key;
	if(txt==zero){
		printf("The data are OK!");
	} else{
		printf("The data are fault!");
	}
	return 0;
} 

還可以完善的,我就不進一步完善了。

一些數學原理

之前有說數學背景,但是那只是爲了能理解其過程,但並不能讓我們明白爲什麼是這樣。
所以這裏擴展了一塊內容,根據興趣閱讀一下吧!

生成多項式

不知道怎麼說,就舉例說明吧。

在這裏插入圖片描述
其對應的除數分別爲:

  1. 11000000000000101
  2. 10001000000100001
  3. 100000100110000010001110110110111

我相信你應該明白了。

爲什麼餘數爲0

我們剛剛介紹了模二除法。
這裏我們再說一個模二加法。

  1. 1+1=0
  2. 1+0=1
  3. 0+0=0
  4. 不進位

爲什麼是這樣定義?
因爲模二。
所以你只要讓結果對2取模,你就知道爲什麼會這樣了。
我們設原數據左移後爲t,除數爲a,商爲s,餘數爲r。
所以有:t=as+r
而我們傳遞的是t+r
即t+r=a
s+r+r
有什麼問題?
我們看一下r+r
注意這裏是模二加法,不進位,且1+1=0+0=0,
而r和r是一樣的,所以模二相加後,就是0!
所以式子變爲:t+r=a*s
這樣的話(t+r)模二除以a,自然餘數爲0。
這就是爲什麼我們可以這麼做。

校驗成功一定沒錯嗎?

不一定!
有可能會出現某幾位錯了,但是判斷結果是對的。
那麼概率是多少呢?
假設FCS爲32位(4字節)
數據我們取爲8000位(1000字節,此處數據大小取了一箇中值,以太網幀中的數據通常爲46~1500字節)(當然了,其實從後面的結果可以知道此處並無影響)
8000位數據,最大爲8000個1,最小爲1(7999個0)。
進行二進制除法,商最大爲7969位,總共有27969個可能的商。
總共可能破壞的結果爲28000.
所以誤判的概率爲P=(27969-1)/28000
也就約爲1/231,即1/2,147,483,648
這概率是多大呢?
差不多是生一個六胞胎的概率吧。
想一想,你身邊有幾個六胞胎?你聽過幾個六胞胎?
因此,其檢錯能力是很強的。重點是算法也很簡單,方便實際應用。

結束語

這就是今天對CRC的探究,喜歡的話點一個讚唄!
有什麼問題也歡迎與我討論!

發佈了231 篇原創文章 · 獲贊 47 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章