惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
美团技术团队
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
F
Fortinet All Blogs
A
About on SuperTechFans
Recent Announcements
Recent Announcements
D
Docker
Vercel News
Vercel News
Engineering at Meta
Engineering at Meta
腾讯CDC
Martin Fowler
Martin Fowler
阮一峰的网络日志
阮一峰的网络日志

博客园 - YD

如何查找某一函数的定义 Ruby 线程--生产者、消费者 Ruby的函数指针 Rails2.0学习--真困难 加班赶工,得不偿失——历史给你上六课 7. 创建Subversion服务之补充 Task Manager 1.1 开源软件:(Task Manager)任务管理-找出自己最需要完成的任务 小说阅读器 2.0 6. 创建 Subversion 服务 小说阅读器,有兴趣的同志可以试一下 不规则窗体的制作 播放 wave 文件 简单 Socket 通信 5. 多人协作 C# 中信号量的使用 4. 不要把不必要的文件版本化 3. 从 Repository 中恢复 2. 创建你的 Repository
CRC 校验 C# 实现法
YD · 2007-05-03 · via 博客园 - YD
 

计算机网络这门课提到过 CRC 检验。用 C# 模拟实现它主要关注模二除法和字节向整型数组的转换。

这里的实现方法效率很低,不过思想却很清晰。先把代码列出来:

    public class Buffoon_CRC_CCITT

    {

        private int[] Multinomial;

        public Buffoon_CRC_CCITT()

        {

            Multinomial = new int[17];

            for (int i = 0; i < 17; ++i)

            {

                if (i == 0 || i == 4 || i == 11 || i == 16) Multinomial[i] = 1;

                else Multinomial[i] = 0;

            }

        }

        public byte[] AddCRCCodes(byte[] bytes)

        {

            int[] source = ToArray(bytes);

            int[] codes = new int[source.Length + Multinomial.Length - 1];

            source.CopyTo(codes, 0);

            int startIndex = 0;

            for (; startIndex < source.Length; ++startIndex)

            {

                if (codes[startIndex] == 1) break;

            }

            for (int i = startIndex; i < source.Length; )

            {

                for (int j = 0; j < Multinomial.Length; ++j)

                {

                    codes[i + j] = (codes[i + j] + Multinomial[j]) % 2;

                }

                for (; i < source.Length; ++i)

                {

                    if (codes[i] == 1) break;

                }

            }

            source.CopyTo(codes, 0);

            return FromArray(codes);

        }

        public bool Verify(byte[] bytes, out byte[] source)

        {

            int[] codes = ToArray(bytes);

            int[] _source = new int[codes.Length - Multinomial.Length + 1];

            Array.Copy(codes, _source, _source.Length);

            int startIndex = 0;

            for (; startIndex < _source.Length; ++startIndex)

            {

                if (codes[startIndex] == 1) break;

            }

            for (int i = startIndex; i < _source.Length; )

            {

                for (int j = 0; j < Multinomial.Length; ++j)

                {

                    codes[i + j] = (codes[i + j] + Multinomial[j]) % 2;

                }

                for (; i < _source.Length; ++i)

                {

                    if (codes[i] == 1) break;

                }

            }

            for (int i = codes.Length - 1; i > codes.Length - Multinomial.Length + 1; --i)

            {

                if (codes[i] == 1)

                {

                    source = null;

                    return false;

                }

            }

            source = FromArray(_source);

            return true;

        }

        private int[] ToArray(byte[] b)

        {

            int[] arr = new int[b.Length * 8];

            for (int i = 0; i < b.Length; ++i)

            {

                arr[i * 8] = (b[i] & 0x80) >> 7;

                arr[i * 8 + 1] = (b[i] & 0x40) >> 6;

                arr[i * 8 + 2] = (b[i] & 0x20) >> 5;

                arr[i * 8 + 3] = (b[i] & 0x10) >> 4;

                arr[i * 8 + 4] = (b[i] & 0x08) >> 3;

                arr[i * 8 + 5] = (b[i] & 0x04) >> 2;

                arr[i * 8 + 6] = (b[i] & 0x02) >> 1;

                arr[i * 8 + 7] = b[i] & 0x01;

            }

            for (int i = 0; i < arr.Length; ++i)

            {

                Debug.Assert(arr[i] == 1 || arr[i] == 0);

            }

            return arr;

        }

        private byte[] FromArray(int[] arr)

        {

            Debug.Assert(arr.Length % 8 == 0);

            for (int i = 0; i < arr.Length; ++i)

            {

                Debug.Assert(arr[i] == 1 || arr[i] == 0);

            }

            byte[] bytes = new byte[arr.Length / 8];

            for (int i = 0; i < arr.Length; i += 8)

            {

                int value = 0;

                for (int j = 0; j < 8; ++j)

                {

                    if (arr[i + j] == 1) value += (int)Math.Pow((double)2, (double)(7 - j));

                }

                bytes[i / 8] = (byte)value;

            }

            return bytes;

        }

    }

As you can see, 我把这个类取名为“小丑_CRC_CCITT”。因为我觉得这个类干得事就是多此一举,没什么用,只会捣乱。不过这个练习对提高对二进制数的控制能力比较有用。

这个程序的思想就是把一个给定的字节数组转换成由 0, 1 组成的整型数组(这个整型数组用来表示二进制序列),然后对这个二进制数组进行运算。所以,一开始先看 FromArray() ToArray() 这两个函数。FromArray() 用来把二进制整型数组转换成字节数组。而 ToArray() 就是把字节数组转换成整型数组。

有这个两个函数垫底,我们就可以看给一个二进制流加上 CRC 校验码了。AddCRCCodes() 就是干这个事的。它用模二除法算出给定的字节数组对应的 CRC 校验码。

多项式用的是 CCITT 标准。即 多项式 = x^16 + x^12 + x^5 + 1。注意构造函数在初始化这个多项式的代码。我用多项式的 0 索引的元素表示最高位。

回过头来继续看 AddCRCCodes()。它把参数转换成整型数组,然后进行模二除法,算出余项。把余项附加到原来的字节数组上。最后把这个附加了余项的字节数组返回。由于 CCITT 的标准和 CRC-16 都是 16 次的多项式,故附加的余项长度正好为 2 个字节。注意,多项式的长度是 17 位,要比余项多一位。

知道如何加 CRC 余项后,校验的过程也就如法炮制了。它对应的函数为 Verify()

这个类在更改了构造函数对 Multinomial (多项式) 的初始化代码后,很容易地变成针对任意一个长度为 8 * n + 1 位多项式的 CRC 校验类。

当然老师验收这个题的时候,要我把结果用手算一遍,再和机器的输出比较。一样的话才算分。幸亏我当时写这个程序的时候注意了通用性。验收的时候把多项式改成 9 位的了。如果用手算 17 的多项式,那……