[技术栈]CRC校验原理及C#代码实现CRC16、CRC32计算FCS校验码
1.CRC、FCS是什么
CRC,全称Cyclic Redundancy Check,中文名称为循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。
FCS,全称Frame Check Sequence,中文名称为帧校验序列,俗称帧尾,即计算机网络数据链路层的协议数据单元(帧)的尾部字段,是一段4个字节的循环冗余校验码。
注:CRC循环冗余校验和FCS帧校验序列是单独的概念,CRC是一种错误校验方法,FCS是帧尾校验码,FCS可以采用CRC校验方法,也可以采用其他校验方法。
2.CRC算法原理
我们可以把任意的一串二进制数据表示为一个与之对应的多项式。比如:
二进制数据:1100101
多项式:\(x^6 + x^5 + x^2+1\)
多项式: \(x^6 + x^4+x^3 + x^2+1\)
二进制数据:1011101
有了这样的对应关系,对二进制数据的CRC校验就可以利用多项式运算规则进行校验计算。
CRC校验算法正是采用了模2除法,在数据处理里的具体表现为异或运算。
CRC的具体运算规则为:假设要传输的二进制数据为:10010110,对应的m阶多项式为:\(M =x^7+x^4+x^2+x^1\),除数为h阶的多项式为:\(H=x^4+x\),对应的二进制码为:10010,先将M乘以\(x^h\),即将M对应的二进制数据后面加h个0,然后除以h阶的多项式H,得到的h-1阶的余数项R对应的二进制数据即为数据10010110的CRC校验码。
3.计算CRC校验
3.1.手工计算CRC校验码
M和H的多项式除法运算,可以用模2除法运算计算。下面为以生成多项式为H求10010110的CRC校验码运算过程:

对应到异或运算:

通过示例即其他自定义的一些数据运算后,根据运算现象总结可以得到一些规律:
1.每次异或运算,当从左到右首位为1的时候,就与生成多项式H异或运算,然后再左移1位;当首位为0的时候只将数据左移1位。
2.每次异或运算后的数据,首位必定为0,因为首位为1的时候进行异或运算,而生成多项式的首位也必定为1。所以当需要进行异或运算时,可以舍弃H的首位,舍弃后为H',直接将数据左移一位后再与H'异或。

3.每次运算,参与运算的是数据的前h位,可以用一个存储h位二进制数据的寄存器S,将数据的前h位存储到这个寄存器中。每次运算先将寄存器的首位移除,然后将二进制数据后一位移入,然后再参与运算,最后寄存器中的值即为CRC校验码。
3.2.C#代码计算CRC校验码
//代码验证如下:
static void Main(string[] args)
{
int data = 0b10010110;
int ploy = 0b0010;
ploy <<= 4;
Console.WriteLine($"第0次运算结果:"+Convert.ToString(data, 2));
for (int i = 0; i < 8; i++)
{
if ((data & 0b10000000) == 0b10000000)
{
data = (data << 1) ^ ploy;
}
else
{
data <<= 1;
}
Console.WriteLine($"第{i+1}次运算结果:"+Convert.ToString(data, 2));
}
Console.WriteLine($" 最终运算结果:"+Convert.ToString(data, 2));
Console.ReadKey();
}

这里用int的第5位到第8位作为一个四位寄存器,可以看到与手算一致,最后算得校验位1100。
4.查表法

可以看到,参与运算的始终只有4位,所以在移位D1数据时,参与运算的数据只有D1和D2,经过四次移位运算,D1被移除寄存器,这个时候受到影响的只有D2。而将D2的初值经过四次异或运算后的值就可以获得四次移位后的新数据\(D2'=D2\bigoplus H1 \bigoplus H2\bigoplus H3\bigoplus H4 = D2 \bigoplus \sum{h}\)。
每一次D2是异或0还是异或生成多项式H',与D2本身的值无关,仅与D1中被移除的数据有关(首位为0还是1),所以这里引入了一个查表法,即先将所有可能的D1组合都计算出对应的\(\sum{h}\),一次性移除四位,然后以\(D2\bigoplus{\sum{h}}\)即可以获得D2'。
生成多项式为H,则一共有\(2^h\)种可能,代码如下:
class CalcByCrcTable
{
private byte[] CrcTable;
private void CteateTable()
{
int ploy = 0b0010;
CrcTable = new byte[(int)Math.Pow(2,4)];
ploy <<= 4;
for (int i = 0; i < CrcTable.Length ; i++)
{
int data = i<<4;
for (int j = 0; j < 4; j++)
{
if ((data & 0b10000000) == 0b10000000)
{
data = (data << 1) ^ ploy;
}
else
{
data <<= 1;
}
}
CrcTable[i] = Convert.ToByte((data & 0xf0)>>4);
}
}
public byte CalcCrc()
{
CteateTable();
int data = 0b10010110;
byte crchigh4 = CrcTable[(data>>4)&0x0f];//用查表法先查到data的高四位1001的crc值;
byte value = Convert.ToByte((data & 0x0f) ^ crchigh4);//将高四位的CRC与低四位异或,得到移位四次后的数据值;
byte crc = CrcTable[value]; //在用移位后的数据值查出数据的CRC校验码;
return crc;
}
}
static void Main(string[] args)
{
CalcByCrcTable calcByCrcTable = new CalcByCrcTable();
byte crc = calcByCrcTable.CalcCrc();
Console.WriteLine($" CRC校验码为:" + Convert.ToString(crc, 2));
Console.ReadKey();
}
//打印结果如下
CRC校验码为:1100
可以看到与前面的计算法结果一致。
5.反向校验
上面所诉的均为正向检验(Normal),当然也有反向校验(Reversed),反向校验是将数据和生成多项式均进行了一个镜像,当然算法也需要镜像,即镜像后从右往左运算。
5.1手工计算CRC反向校验码
原二进制数据:10010110
原生成多项式:0010
正向CRC校验码:1100
镜像二进制数据:01101001
镜像生成多项式:0100
镜像算法:

反向CRC校验码:0011
5.2.C#代码计算CRC反向校验码
class CalcByCrcTable
{
private byte[] CrcTable;
private void CteateReversedTable()
{
int ploy = 0b0100;
CrcTable = new byte[(int)Math.Pow(2, 4)];
for (int i = 0; i < CrcTable.Length; i++)
{
int data = i;
for (int j = 0; j < 4; j++)
{
if ((data & 1) == 1)
{
data = (data >> 1) ^ ploy;
}
else
{
data >>= 1;
}
}
CrcTable[i] = Convert.ToByte((data & 0x0f));
}
}
public byte CalcReversedCrc()
{
CteateReversedTable();
int data = 0b01101001;
byte crclow4 = CrcTable[data & 0x0f];//用用查表法先查到data的低四位1001的crc值;
byte value = Convert.ToByte(((data>>4) & 0x0f) ^ crclow4);//将第四位的CRC与低四位异或,得到移位四次后的数据值;
byte crc = CrcTable[value]; //在用移位后的数据值查出数据的CRC校验码;
return crc;
}
}
static void Main(string[] args)
{
CalcByCrcTable calcByCrcTable = new CalcByCrcTable();
byte crc = calcByCrcTable.CalcReversedCrc();
Console.WriteLine($" CRC反向校验码为:" + Convert.ToString(crc, 2));
Console.ReadKey();
}
//打印结果如下
CRC反向校验码为:11
6.C#查表法计算CRC16校验码
//多线程使用时请注意干扰
class CalcOnCrc16
{
private ushort[] Crc16NormalTable;
private ushort[] Crc16ReversedTable;
private void CreateNormalCrc16Table(ushort ploy)
{
ushort data;
Crc16NormalTable = new ushort[256];
int i, j;
for (i = 0; i < 256; i++)
{
data = (ushort)(i << 8);
for (j = 0; j < 8; j++)
{
if ((data & 0x8000) == 0x8000)
data = Convert.ToUInt16((ushort)(data << 1) ^ ploy);
else
data <<= 1;
}
Crc16NormalTable[i] = data;
}
}
private void CreateReversedCrc16Table(ushort ploy)
{
ushort data;
Crc16ReversedTable = new ushort[256];
int i, j;
for (i = 0; i < 256; i++)
{
data = (ushort)i;
for (j = 0; j < 8; j++)
{
if ((data & 1) == 1)
data = Convert.ToUInt16((ushort)(data >>1) ^ ploy);
else
data >>= 1;
}
Crc16ReversedTable[i] = data;
}
}
/// <summary>
/// 正向计算CRC16校验码
/// </summary>
/// <param name="bytes">校验数据</param>
/// <param name="poly">生成多项式</param>
/// <param name="crcInit">校验码初始值</param>
/// <returns></returns>
public ushort CalcNoemalCrc16(byte[] bytes,ushort poly,ushort crcInit)
{
CreateNormalCrc16Table(poly);
ushort crc = crcInit;
for (int i = 0; i < bytes.Length; i++)
{
crc = Convert.ToUInt16((ushort)(crc << 8) ^ Crc16NormalTable[((crc >> 8) & 0xff) ^ bytes[i]]);
}
return crc;
}
/// <summary>
/// 反向计算CRC16校验码
/// </summary>
/// <param name="bytes">校验数据</param>
/// <param name="poly">反向生成多项式</param>
/// <param name="crcInit">校验码初始值</param>
/// <returns></returns>
public ushort CalcReversedCrc16(byte[] bytes, ushort poly, ushort crcInit)
{
CreateReversedCrc16Table(poly);
ushort crc = crcInit;
for (int i = 0; i < bytes.Length; i++)
{
crc = Convert.ToUInt16((ushort)(crc >> 8) ^ Crc16ReversedTable[(crc & 0xff) ^ bytes[i]]);
}
return crc;
}
}
7.C#查表法计算CRC32校验码
class CalcOnCrc32
{
private uint[] Crc32NormalTable;
private uint[] Crc32ReversedTable;
private void CreateNormalCrc32Table(uint ploy)
{
uint data;
Crc32NormalTable = new uint[256];
int i, j;
for (i = 0; i < 256; i++)
{
data = (uint)(i << 24);
for (j = 0; j < 8; j++)
{
if ((data & 0x80000000) == 0x80000000)
data = Convert.ToUInt32((uint)(data << 1) ^ ploy);
else
data <<= 1;
}
Crc32NormalTable[i] = data;
}
}
private void CreateReversedCrc32Table(uint ploy)
{
uint data;
Crc32ReversedTable = new uint[256];
int i, j;
for (i = 0; i < 256; i++)
{
data = (uint)i;
for (j = 0; j < 8; j++)
{
if ((data & 1) == 1)
data = Convert.ToUInt32((uint)(data >> 1) ^ ploy);
else
data >>= 1;
}
Crc32ReversedTable[i] = data;
}
}
/// <summary>
/// 正向计算CRC32校验码
/// </summary>
/// <param name="bytes">校验数据</param>
/// <param name="poly">生成多项式</param>
/// <param name="crcInit">校验码初始值</param>
/// <returns></returns>
public uint CalcNoemalCrc32(byte[] bytes, uint poly, uint crcInit)
{
CreateNormalCrc32Table(poly);
uint crc = crcInit;
for (int i = 0; i < bytes.Length; i++)
{
crc = Convert.ToUInt32((uint)(crc << 8) ^ Crc32NormalTable[((crc >> 24) & 0xff) ^ bytes[i]]);
}
return crc;
}
/// <summary>
/// 反向计算CRC32校验码
/// </summary>
/// <param name="bytes">校验数据</param>
/// <param name="poly">反向生成多项式</param>
/// <param name="crcInit">校验码初始值</param>
/// <returns></returns>
public uint CalcReversedCrc32(byte[] bytes, uint poly, uint crcInit)
{
CreateReversedCrc32Table(poly);
uint crc = crcInit;
for (int i = 0; i < bytes.Length; i++)
{
crc = Convert.ToUInt32((uint)(crc >> 8) ^ Crc32ReversedTable[(crc & 0xff) ^ bytes[i]]);
}
return crc;
}
}
参考资料
[技术栈]CRC校验原理及C#代码实现CRC16、CRC32计算FCS校验码的更多相关文章
- JHipster技术栈理解 - UAA原理分析
本文简要分析了UAA的认证机制和部分源码功能. UAA全称User Account and Authentication. 相关源码都是通过Jhipster生成,包括UAA,Gateway,Ident ...
- CRC的校验原理
一.基本原理 CRC检验原理实际上就是在一个p位二进制数据序列之后附加一个r位二进制检验码(序列),从而构成一个总长为n=p+r位的二进制序列:附加在数据序列之后的这个检验码与数据序列的内容之间存在着 ...
- CRC码计算及校验原理的最通俗诠释
5.3.2 循环冗余校验检错方案 奇偶校验码(PCC)只能校验一位错误,本节所要介绍的循环冗余校验码(CRC)的检错能力更强,可以检出多位错误. 1. CRC校验原理 CRC校验原理看起来比 ...
- CRC 校验原理及步骤
什么是 CRC 校验? CRC 即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计 ...
- CRC校验原理及步骤
什么是CRC校验? CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并 ...
- CRC校验原理
此文为转载文,原作者博客传送门 CRC校验原理 CRC校验原理看起来比较复杂,好难懂,因为大多数书上基本上是以二进制的多项式形式来说明的.其实很简单的问题,其根本思想就是先在要发送的帧后面附加一个数( ...
- iOS 覆盖率检测原理与增量代码测试覆盖率工具实现
背景 对苹果开发者而言,由于平台审核周期较长,客户端代码导致的线上问题影响时间往往比较久.如果在开发.测试阶段能够提前暴露问题,就有助于避免线上事故的发生.代码覆盖率检测正是帮助开发.测试同学提前发现 ...
- 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? .Net Web开发技术栈
通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 什么是.NET?什么是.NET Framework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念 ...
- Java 技术栈中间件优雅停机方案设计与实现全景图
欢迎关注公众号:bin的技术小屋,阅读公众号原文 本系列 Netty 源码解析文章基于 4.1.56.Final 版本 本文概要 在上篇文章 我为 Netty 贡献源码 | 且看 Netty 如何应对 ...
随机推荐
- Egret白鹭开发微信小游戏分享功能
今天给大家分享一下微信分享转发功能,话不多说,直接干 方法一: 1.在egret中打开Platfrom.ts文件,添加代码如下(当然,你也可以直接复制粘贴) /** * 平台数据接口. * 由于每款游 ...
- C#数据结构_查找
查找:静态查找和动态查找. 衡量查找算法的最主要的标准是平均查找长度(Average Search Length,简 称 ASL).平均查找长度是指在查找过程中进行的关键码比较次数的平均值. 顺序查找 ...
- 如何理解JS中this指向的问题
首先,用一句话解释this,就是:指向执行当前函数的对象. 当前执行,理解一下,也就是说this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定.this到底指向谁?this的最终指向的 ...
- 关于Springboot+thymeleaf +MybatisPlus 报错Error resolving template [index], template might not exist的问题解决
这个问题困扰了我整整一上午,各种方式,什么返回路径 ,静态资源啊 什么的,能想到的都去搞了,可是问题还是解决不了!!!我查看了一下编译文件的[target]文件夹!发现了问题所在!根本就没有编译进去! ...
- CF #535 (Div. 3) E2 Array and Segments (Hard version) 利用线段树进行区间转移
传送门 题意: 有m个区间,n个a[ i ] , 选择若干个区间,使得整个数组中的最大值和最小值的差值最小.n<=1e5,m<=300; 思路: 可以知道每个i,如果一个区间包含这个 ...
- CCPC 网络赛
array 做法 比赛中的表现..... 已经无法言语形容了. 题意是,查询前缀中大于某个数字的 mex,在线. 一下把问题转化为偏序问题.... 带修主席树?????这下好,直接一箭穿心,武将被移除 ...
- HDU1814Peaceful Commission求2-sa最小字典序
#include <iostream> #include <cstdio> #include <vector> #include <cstring> # ...
- ACM-ICPC 2018 徐州赛区(网络赛)
目录 A. Hard to prepare B.BE, GE or NE F.Features Track G.Trace H.Ryuji doesn't want to study I.Charac ...
- 牛客暑假多校 H Prefix sum
题意: 现在有一个2维矩阵, 初始化为0. 并且这个矩阵是及时更新的. dp[i][j] = dp[i-1][j] + dp[i][j-1]; 现在有2种操作: 0 x y dp[1][x] += ...
- codeforces 591 E. Three States(bfs+思维)
题目链接:http://codeforces.com/contest/591/problem/E 题意:有3个数字表示3个城市,每种城市都是相互连通的,然后不同种的城市不一定联通,'.'表示可以建设道 ...