[技术栈]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 如何应对 ...
随机推荐
- springboot报 org.thymeleaf.exceptions.TemplateInputException: Error resolving template "succeed";
--------------------- 本文转自 林晓风 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/Lin_xiaofeng/article/details/ ...
- java.lang.TypeNotPresentException: Type javax.xml.bind.JAXBContext not present解决方案
因为JAXB-API是java ee的一部分,在jdk9中没有在默认的类路径中: java ee api在jdk中还是存在的,默认没有加载而已,jdk9中引入了模块的概念,可以使用 模块命令--add ...
- vue-cli2.X中引入高德地图,将其设为全局对象
平时一般未用脚手架构建或用webpack构建的项目只要用script标签引入即可,在vue-cli 2.X构建的项目中,需要用如下方式引入高德地图: 首先在高德地图开放平台中注册开发者账号并获取key ...
- Java之Exception
Exception这个东西,程序中必须会有的,尽管我们很不乐意看到它,可是从另一个角度考虑,有异常则说明程序有问题,有助于我们及时改正.有的时候程序出错的原因有很多,比如不合法的输入.类型.空指针甚至 ...
- React-native 关于 android真机 出现连不上服务器
我们都知道使用RN开发移动端应用时,我们要在手机端运行程序,可以下载 expo 这个软件进行扫描二维码连接到开发的APP上 有时会有突然连不上之前连上过的应用,出现如下画面 首先保证你的电脑和你的手机 ...
- MySQL8版本密码重置(老版本skip-grant-tables不起作用,MySQL服务开启之后立马关闭)
原文:https://blog.csdn.net/gupao123456/article/details/80766154 MySQL密码重置思路MySQL的密码是存放在user表里面的,修改密码其 ...
- 【Leetcode】【简单】【189. 旋转数组】【JavaScript】
189. 旋转数组 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6,7] 和 k = 3输出: [5,6,7,1,2,3,4]解释 ...
- 2019 Multi-University Training Contest 9
A. Rikka with Quicksort 题意 求 EX 快速排序复杂度. 做法 根据线性期望可加性,独立考虑长度为 \(m\) 的区段对答案的贡献.进行简单的公式推导,对 \(s(x)=\su ...
- Ubuntu开机出现grub指令,无法正常开机
问题 最近开机出现了如下的界面: 分析问题 首先看看GNU GRUB是什么东东?干什么用的? GNU GRUB是多重引导加载程序.通俗点说,它就是用来一个可以让你选择运行什么操作系统的程序. 在你开机 ...
- Java中时间API使用详解
目录 Java中时间API使用详解 1. 时区概念 2. 几种常见的时间 3. 时间戳 4. Java中的时间API 5. Java8中新添加的时间API 6. 在东八区的机器上获得美国时间 Java ...