传输数据校验算法研究

 今天简单介绍一些传输数据校验的方法,就昨天整理的资料和就我的理解写的Demo做个总结!希望大家多多指教!

定义

通俗的说,就是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,说明数据是完整的。

实际应用

防止自己的程序被篡改。

有些可执行程序,当被改了资源时再运行会有文件已损坏的提示,这就是使用了数据校验。本例是用md5做为数据校验的算法。当然你可以使用个性化的

比如des作为数字签名,那样安全性更高。

校验方法

数据校验无非就是三个步骤:一、添加校验码;二、校验数据;三、还原数据。   如下图:(具体方法后面一一介绍)

先看看这次咱们学习哪些,我在里面定义的一个枚举

        public enum VerifyType
        {
            /// <summary>
            /// 无校验
            /// </summary>
            None,
            /// <summary>
            /// 奇校验
            /// </summary>
            Odd,
            /// <summary>
            /// 偶校验
            /// </summary>
            Even,
            /// <summary>
            /// 1校验
            /// </summary>
            Mark,
            /// <summary>
            /// 0校验
            /// </summary>
            Space,
            /// <summary>
            /// 循环冗余码CRC检验
            /// </summary>
            CRC,
            /// <summary>
            /// 异或校验
            /// </summary>
            BCC,
            /// <summary>
            /// 和校验
            /// </summary>
            Sum,
            /// <summary>
            /// MD5
            /// </summary>
            MD5
        }

然后就是对应这三个方法的测试调用了:

 static void Main(string[] args)
        {
            //校验方式选择
            Verify.VerifyType T = Verify.VerifyType.None;

            Console.WriteLine("验证方式{0} :\n==================================================================\n\n", T);

            byte[] sendDate = { 10, 252, 253, 254, 255, 50, 51, 66, 85, 11 };
            Console.WriteLine("待发送数据:");
            for (int i = 0; i < sendDate.Length; i++)
            {
                Console.Write(sendDate[i] + " ");
            }
            Console.WriteLine();

            //添加校验码
            sendDate = Verify.AddCode(sendDate, T);
            Console.WriteLine("增加校验码的发送数据:");
            for (int i = 0; i < sendDate.Length; i++)
            {
                Console.Write(sendDate[i] + " ");
            }
            Console.WriteLine("\n…………………………………………………………………………………………………………\n\n");

            Console.WriteLine("接收数据校验:------------");
            if (Verify.CheckCode(sendDate, T))
            {
                Console.WriteLine("校验成功");
                sendDate = Verify.RestoringData(sendDate, T);
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("校验失败!!!!");
            }
            for (int i = 0; i < sendDate.Length; i++)
            {
                Console.Write(sendDate[i] + " ");
            }

            Console.ReadKey();
        }

串口通讯过程中有五种校验方式,分别是无校验(None),奇校验(Odd),偶校验(Even),1校验(Mark),0校验(Space)。

无校验(None)就是直接发送数据没有校验位,所以没什么好介绍的,直接看看运行结果吧!

奇偶校验Parity Check (下面介绍来自百度百科)

偶校验(Parity Check)是一种校验代码传输正确性的方法。根据被传输的一组二进制代码的数位中“1”的个数是奇数或偶数来进行校验。采用奇数的称为奇校验,反之,称为偶校验。采用何种校验是事先规定好的。通常专门设置一个奇偶校验位,用它使这组代码中“1”的个数为奇数或偶数。若用奇校验,则当接收端收到这组代码时,校验“1”的个数是否为奇数,从而确定传输代码的正确性。

奇偶校验能够检测出信息传输过程中的部分误码(奇数位误码能检出,偶数位误码不能检出),同时,它不能纠错。在发现错误后,只能要求重发。但由于其实现简单,仍得到了广泛使用。

为了能检测和纠正内存软错误,首先出现的是内存“奇偶校验”。内存中最小的单位是比特,也称为“位”,位只有两种状态分别以1和0来标示,每8个连续的比特叫做一个字节(byte)。不带奇偶校验的内存每个字节只有8位,如果其某一位存储了错误的值,就会导致其存储的相应数据发生变化,进而导致应用程序发生错误。而奇偶校验就是在每一字节(8位)之外又增加了一位作为错误检测位。在某字节中存储数据之后,在其8个位上存储的数据是固定的,因为位只能有两种状态1或0,假设存储的数据用位标示为1、1、1、0、0、1、0、1,那么把每个位相加(1+1+1+0+0+1+0+1=5),结果是奇数。对于偶校验,校验位就定义为1,反之则为0;对于奇校验,则相反。当CPU读取存储的数据时,它会再次把前8位中存储的数据相加,计算结果是否与校验位相一致。从而一定程度上能检测出内存错误,奇偶校验只能检测出错误而无法对其进行修正,同时虽然双位同时发生错误的概率相当低,但奇偶校验却无法检测出双位错误。

简单例子:

奇校验(Odd),这里是应用在软件传输上的校验而不是硬件编程而且是例子所以写的比较简单易懂

算法:

 1         /// <summary>
 2         /// 奇校验算法获得校验码
 3         /// </summary>
 4         /// <param name="date"></param>
 5         /// <returns></returns>
 6         private static byte[] OddCode(byte[] data)
 7         {
 8             byte[] bVerify = new byte[data.Length / 8 + 2];//校验位接收数组
 9             bVerify[bVerify.Length - 1] = (byte)bVerify.Length;//校验位长度
10             //计算校验位数据
11             for (int i = 0; i < data.Length; i++)//每个位去处理
12             {
13                 if (GetOddEvenVerify(data[i]))
14                 {//奇数 //0;
15                     clr_bit(ref bVerify[i / 8], i);
16                 }
17                 else
18                 {//偶数//1;
19                     set_bit(ref bVerify[i / 8], i);
20                 }
21             }
22             return bVerify;
23         }
24         /// <summary>
25         /// 判断当前位1的个数是奇数个还是偶数个
26         /// </summary>
27         /// <param name="bData"></param>
28         /// <returns>True 为奇数个 False 为偶数个</returns>
29         private static bool GetOddEvenVerify(byte bData)
30         {
31             byte bcCount = 0;    /* 字节内1的个数 */
32
33             for (int i = 0; i < 8; i++)
34             {
35                 if ((bData & (byte)0x01) == 1)
36                 {
37                     bcCount++;
38                 }
39                 bData >>= 1;
40             }
41
42             return ((bcCount & (byte)0x01) == 1);
43         }
44         /// <summary>
45         /// 置位x的y位 
46         /// </summary>
47         /// <param name="x"></param>
48         /// <param name="y"></param>
49         private static void set_bit(ref byte x, int y)
50         {
51             x = (byte)(x | (0x01 << (y)));
52
53         }
54         /// <summary>
55         /// 清零x的y位 
56         /// </summary>
57         /// <param name="x"></param>
58         /// <param name="y"></param>
59         private static void clr_bit(ref byte x, int y)
60         {
61             x = (byte)(x & ~(0x01 << (y)));
62         }

无论是奇校验还是偶校验都是需要统计校验位数据中1的个数GetOddEvenVerify,还有就是对校验码相应位进行标记为1还是0的方法 set_bit 和 clr_bit 这三个公用的方法,然后就是对原始数据进行计算得到校验码并附到原始数据后面(当然你也可以放到前面)。

在这我就用了1byte 作为标记校验位的长度所以能标记的范围是很有限的只有255位

添加校验 :

             case VerifyType.Odd:
                    if ((data.Length / 8 + 1) > 255) return newDate;//校验位长度大于255就超出这次比较算法处理范围---收敛

                    byte[] bVerifyOdd = OddCode(data);
                    newDate = new byte[data.Length + bVerifyOdd.Length];
                    data.CopyTo(newDate, 0);
                    bVerifyOdd.CopyTo(newDate, data.Length);
                    break;

校验数据 :

            case VerifyType.Odd:
                    int lenOdd = data[data.Length - 1];
                    byte[] sourceBVerifyOdd = new byte[lenOdd];
                    sourceBVerifyOdd = data.Skip(data.Length - lenOdd).Take(lenOdd).ToArray();
                    byte[] nowBVerifyOdd = OddCode(RestoringData(data, t));
                    return sourceBVerifyOdd.SequenceEqual(nowBVerifyOdd);

还原数据 :(奇校验 偶校验数据还原都是一样的算法)

                case VerifyType.Odd:
                case VerifyType.Even:
                    return data.Take(data.Length - (data[data.Length - 1])).ToArray();    

运行结果:

偶校验(Even)

算法:(与奇校验算法唯一不同的就是补码位的0和1正好相反)

        /// <summary>
        /// 偶校验算法获得校验码
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private static byte[] EvenCode(byte[] data)
        {
            byte[] bVerify = new byte[data.Length / 8 + 2];//校验位接收数组
            bVerify[bVerify.Length - 1] = (byte)bVerify.Length;//校验位长度
            //计算校验位数据
            for (int i = 0; i < data.Length; i++)//每个位去处理
            {
                if (GetOddEvenVerify(data[i]))
                {//奇数 //1;
                    set_bit(ref bVerify[i / 8], i);
                }
                else
                {//偶数//0;
                    clr_bit(ref bVerify[i / 8], i);
                }
            }
            return bVerify;
        }

添加校验码:

                case VerifyType.Even:
                    if ((data.Length / 8 + 1) > 255) return newDate;//校验位长度大于255就超出这次比较算法处理范围---收敛

                    byte[] bVerifyEven = EvenCode(data);
                    newDate = new byte[data.Length + bVerifyEven.Length];
                    data.CopyTo(newDate, 0);
                    bVerifyEven.CopyTo(newDate, data.Length);
                    break;

检验校验码:

              case VerifyType.Even:
                    int lenEven = data[data.Length - 1];
                    byte[] sourceBVerifyEven = new byte[lenEven];
                    sourceBVerifyEven = data.Skip(data.Length - lenEven).Take(lenEven).ToArray();
                    byte[] nowBVerifyEven = EvenCode(RestoringData(data, t));
                    return sourceBVerifyEven.SequenceEqual(nowBVerifyEven);

运行结果:

1校验(Mark),0校验(Space)

校验方式设置为1校验(Mark),校验位固定为1;如果校验方式设置为0校验(Space),校验位固定为0;

添加校验码:

                case VerifyType.Mark:
                    newDate = new byte[data.Length + 1];
                    data.CopyTo(newDate, 0);
                    newDate[data.Length] = 1;
                    break;
                case VerifyType.Space:
                    newDate = new byte[data.Length + 1];
                    data.CopyTo(newDate, 0);
                    newDate[data.Length] = 0;
                    break;

检验校验码:

                case VerifyType.Mark:
                    return data[data.Length - 1] == 1;
                case VerifyType.Space:
                    return data[data.Length - 1] == 0;

数据还原:

                case VerifyType.Mark:
                case VerifyType.Space:
                case VerifyType.BCC:
                    return data.Take(data.Length - 1).ToArray();

运行结果:

bcc异或校验法(block check character)

实现方法:很多基于串口的通讯都用这种既简单又相当准确的方法。它就是把所有数据都和一个指定的初始值(通常是0)异或一次,最后的结果就是校验值,通常把它附在通讯数据的最后一起发送出去。接收方收到数据后自己也计算一次异或和校验值,如果和收到的校验值一致就说明收到的数据是完整的。

适用范围:适用于大多数要求不高的数据通讯

应用例子:ic卡接口通讯、很多单片机系统的串口通讯都使用。

算法:

        /// <summary>
        /// 异或校验 校验码
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static byte ParityCode(byte[] data)
        {
            byte CheckCode = 1;
            //异或校验
            for (int i = 0; i < data.Length; i++)
            {
                CheckCode ^= data[i];
            }
            return CheckCode;
        }

添加校验码:

                case VerifyType.BCC:
                    newDate = new byte[data.Length + 1];
                    data.CopyTo(newDate, 0);
                    newDate[data.Length] = ParityCode(data);
                    break;

数据校验:

                case VerifyType.BCC:
                    return (ParityCode(RestoringData(data, t)) == data[data.Length - 1]);

数据还原与之前 1校验和0校验一样,截去最后一位

运行结果:

和校验

这里拿一个int值作为保存结果所以是32位的4byte的校验码

算法:

        /// <summary>
        /// 累加和
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static byte[] SumCode(byte[] data)
        {
            int sum = 0;
            foreach (var item in data)
            {
                sum += item;
            }
            byte[] code = intToBytes(sum);
            return code;
        }

添加校验码:

                case VerifyType.Sum:
                    newDate = new byte[data.Length + 4];
                    data.CopyTo(newDate, 0);
                    SumCode(data).CopyTo(newDate, data.Length);
                    break;

数据校验:

                case VerifyType.Sum:
                    //方法一:
                    //int sourceSum = byteToInt(data.Skip(data.Length - 4).ToArray());
                    //int newSum = byteToInt(SumCode(RestoringData(data, t)));
                    //return sourceSum.Equals(newSum);
                //方法二:
                return data.Skip(data.Length - 4).ToArray().SequenceEqual(SumCode(RestoringData(data, t)));        

还原数据:

                case VerifyType.Sum:
                    return data.Take(data.Length - 4).ToArray();

运行结果:

MD5校验和数字签名

实现方法:主要有md5和des算法。

适用范围:数据比较大或要求比较高的场合。如md5用于大量数据、文件校验,des用于保密数据的校验(数字签名)等等。

应用例子:文件校验、银行系统的交易数据

算法:(这里用的是32位MD5算法)

        /// <summary>
        /// 计算data字节数组的哈希值
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static byte[] MD5Code(byte[] data)
        {
            System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            return md5.ComputeHash(data);//计算data字节数组的哈希值
        }

添加校验码(数据签名):

                case VerifyType.MD5:
                    byte[] md5data = MD5Code(data);//计算data字节数组的哈希值
                    newDate = new byte[data.Length + md5data.Length];
                    data.CopyTo(newDate, 0);
                    md5data.CopyTo(newDate, data.Length);
                    break;

数据校验:

                case VerifyType.MD5:
                    byte[] sourceMD5 = data.Skip(data.Length - 16).ToArray();
                    byte[] newMD5 = MD5Code(RestoringData(data, t));//计算data字节数组的哈希值
                    return sourceMD5.SequenceEqual(newMD5);

数据还原:

                case VerifyType.MD5:
                    return data.Take(data.Length - 16).ToArray();

运行结果:

crc循环冗余校验(Cyclic Redundancy Check)

实现方法:这是利用除法及余数的原理来进行错误检测的.将接收到的码组进行除法运算,如果除尽,则说明传输无误;如果未除尽,则表明传输出现差错。crc校验具还有自动纠错能力。

crc检验主要有计算法和查表法两种方法,网上很多实现代码。今天下班了,我这里就不写了,只要知道算法的思路就可以很容易写出来的。

适用范围:CRC-12码通常用来传送6-bit字符串;CRC-16及CRC-CCITT码则用是来传送8-bit字符。CRC-32:硬盘数据,网络传输等。

应用例子:rar,以太网卡芯片、MPEG解码芯片中

时间: 2024-08-04 14:07:35

传输数据校验算法研究的相关文章

关于校验算法的学习(学习以太网时遇到的)

关于校验算法的学习(学习以太网时遇到的)以太网的包头还是数据都会有校验,在FPGA上实现IP核时,用的是CRC校验,然后学习ICMP协议时,也看到一种校验算法,以此学习分析一下背后原理. 一.先看看ICMP的包格式,主要是写ping命令是用到(会有一篇专门将ping的c语言实现),所以只是展示与ping命令背后的ICMP规则. (来自维基) 对于校验的研究暂时不需要理解其他数据的意思,维基对checksum的解释为 Checksum Error checking data, calculated

基本算法研究1-冒泡排序算法测试

基本算法研究1-冒泡排序算法测试 1.经典冒泡排序法基本原理 先看一个动态图,感觉比较形象: 冒泡排序(Bubble Sort)是一种简单的排序算法.默认是从小到大排序,即把最大的数据排在最后,相当于每次把最大数据像气泡一样浮到水面一样.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换. 基本步骤: 1.比较相邻的元素.如果第一个比第二个大,就交换他们两个.        2.对每一对相邻元素作同样的工作,从开始第一对

HTML5游戏开发-扫雷及其算法研究

吕蒙曰:士隔三月[1],当刮目相看.所以,在下在这三月中发奋图强,花了约莫8节信息课的时间研究扫雷.呜呼,由于在下才能尚且不足,所以也就只能勉强打过中级难度的吧.不过,一边玩的同时,我还一边对扫雷这个游戏的制做方法构思了一下.所以说,本文中的算法完全是凭借自己对扫雷游戏规则的总结而自行研发出来的,倘若和MS的扫雷玩法有些出入,还望各位看官见谅. [1]出自<孙权劝学>,原文为"士别三日",由于在下这三个月来都不曾发表博客,所以引申到"士隔三月",各位看官

Supervised Descent Method Face Alignment 代码下载 和 算法研究 之一

1 主要内容: Supervised Descent Method and its Applications to Face Alignment算法研究. 2代码彩蛋:我问了好久,xxiong好心人发给我的,希望能对你们学习有帮助: 低调下载: http://humansensing.cs.cmu.edu/xxiong/mexintraface1.3.1%28release%29.zip. 注意杜绝一切商业用途,如果需要商业用途,请联系作者本人!! 3本文分为几个部分: (1)解决什么问题 (2

通用高校排课算法研究----3.基于时间片优先级排课算法

通用高校排课算法研究----3.基于时间片优先级排课算法 3   基于时间片优先级排课算法描述与分析 排课问题实质上是时间.教师.班级.教室.课程这五维关系的冲突问题,要合理的解决这个问题首先要了解排课中的一些基本原则以及排课的一些基本要求. 3.1排课中的基本原则 在课程的编排中应遵循一定的规则, 只有按照基本规则来进行课程的编排才能够减少冲突的发生, 这些基本规则主要有以下几条: 1) 同一班级的学生在同一时间(某些特定的选修课时间除外) 不能安排两门课程 2) 同一教师在同一时间不能安排两

校验算法之二进制反码求和

IP/ICMP/IGMP/TCP/UDP等协议的校验和算法都是相同的,算法如下: 在发送数据时,为了计算数IP据报的校验和.应该按如下步骤:    (1)把IP数据报的首部都置为0,包括校验和字段.    (2)把首部看成以16位为单位的数字组成,依次进行二进制反码求和.    (3)把得到的结果存入校验和字段中.    在接收数据时,计算数据报的校验和相对简单,按如下步骤:    (1)把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段.    (2)检查计算出的校验和

程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大系列集锦

程序员面试.算法研究.编程艺术.红黑树.数据挖掘5大系列集锦

GSM07.10协议中串口复用使用的校验算法

1 const static unsigned char __crctable[256] = 2 { 3 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 4 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 5 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 6 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,

基础典型算法研究:合并有序数组

做leetcode第二题的时候,发现合并有序数组是一个很有意思的问题,于是,总结如下,部分内容来源于网络各位大神. 第一种方法: 合并调用sort. 即是将两个数组合并在一个数组里面,然后对合并后的数组调用sort函数即可. class Solution: def getArray(self, A, B) : for item in B : A.append(item) A.sort() 第二种方法: 极值插入法. #include <stdio.h> void insert(int *arr