物聯網平台設計心得:你所不知道的CRC校正

來源:互聯網
上載者:User

標籤:

在物聯網平台設計過程中,我的中介軟體一方面需要處理來自於硬體端的包,另一方面需要處理來自於使用者端的包,使用者端包括web端和手機端等等。所以編寫一個統一的CRC認證是非常必須要。

那麼,在設計開始,CRC認證到底是什麼呢?所謂的CRC認證,就是指,在硬體端或者使用者端進行資料轉送前,通過一套演算法,將待傳輸的資料,通過加驗,算出其校正碼,附加在包體的最後,然後中介軟體收到此包後,對包進行解析,拿出其中的資料內容部分,然後對包重新進行一次CRC加驗,如果本次加驗結果和包體附帶的CRC校正碼資料一致,那麼就說明此條資料包是完整的,可用的。反之則證明此包有問題。

所以從上面過程中,我們可以看出CRC包含這麼幾個重要的內容:CRC產生演算法,CRC解析演算法。

CRC查表演算法

首先,CRC產生演算法如下,從網上隨便都能搜出一堆,我們這裡用的是CRC16位的:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace DSMiddlewire.Lib{    public class CRC16    {        private const int CRC_LEN = 0;        // Table of CRC values for high-order byte        private readonly byte[] _auchCRCHi = new byte[]        {            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40        };        // Table of CRC values for low-order byte        private readonly byte[] _auchCRCLo = new byte[]        {            0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,             0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,             0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,             0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,             0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,             0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,             0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,             0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,             0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,             0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,             0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,             0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,             0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,             0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,             0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,             0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,             0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,             0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,             0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,             0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,             0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,             0x43, 0x83, 0x41, 0x81, 0x80, 0x40        };        /// <summary>        /// 獲得CRC16效驗碼        /// </summary>        /// <param name="buffer"></param>        /// <returns></returns>        internal ushort CalculateCrc16(byte[] buffer)        {            byte crcHi = 0xff;  // high crc byte initialized            byte crcLo = 0xff;  // low crc byte initialized             for (int i = 0; i < buffer.Length - CRC_LEN; i++)            {                int crcIndex = crcHi ^ buffer[i]; // calculate the crc lookup index                crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);                crcLo = _auchCRCLo[crcIndex];            }            return (ushort)(crcHi << 8 | crcLo);        }        /// <summary>        /// 獲得CRC16效驗碼        /// </summary>        /// <param name="strPar"></param>        /// <returns></returns>        public static string CalculateCrc16(string strPar)        {            string retStr = new CRC16().CalculateCrc16(System.Text.Encoding.Default.GetBytes(strPar)).ToString();            while (retStr.Length < 5)            {                retStr = "0" + retStr;            }            return retStr;        }    }}

上面就是利用查表法來產生CRC16校正碼的函數,其機理就是:我通過運算會得到一個CRC校正碼,然後除以256會得到高位值,餘256會得到低位值,最後將高位值和低位值進行或操作,就是我們的校正碼了。

CRC加驗方法

上面的函數雖然可用,但是產生的卻是String類型的值,在資料轉送過程中,都是要轉化為byte傳輸的,所以這裡我們需要將得到的數值轉化為byte類型,然後放到資料內容的byte數組中,傳送給中介軟體:

            /*            * 向底層硬體發送的CRC封裝方法            * 1.var crcGenerate = GetCRC(result); 先得到crc碼:14321            * 2.得到高位,低位值: 14321/256 = 55, 14321%256=241            * 3.轉為byte,追加到最後兩位byte數組中,傳給硬體即可            * */        public static byte[] ConstructMessageWithCRC(string message)        {            //資訊主體,加上一個分隔字元            var messageToConstruct = message + "|";            //算出crc碼            var crcCode = CRC16.CalculateCrc16(messageToConstruct);            var crcCodeShort = ushort.Parse(crcCode);            //CRC碼高位            var crcHigh = byte.Parse((crcCodeShort / 256).ToString());            //CRC碼低位            var crcLow = byte.Parse((crcCodeShort % 256).ToString());            var messageBytes = Encoding.Default.GetBytes(messageToConstruct);            var messageLength = messageBytes.Length;            var messageBytesWithCRC = new byte[messageLength + 2];            Array.Copy(messageBytes, 0, messageBytesWithCRC, 0, messageLength);            //放CRC碼到數組最後兩位            messageBytesWithCRC[messageLength] = crcHigh;            messageBytesWithCRC[messageLength + 1] = crcLow;            return messageBytesWithCRC;        }

我解釋下上面的代碼:messageToConstruct 是我們的資料內容部分,我們先是將資料內容通過CRC16.CalculateCrc16函數算出其校正碼,然後將其最高位和最低位分別放入byte數組中,然後傳送出去。

CRC校正方法

那麼既然有CRC校正碼的產生,也需要CRC校正碼的解析過程,所謂有攻必有防:

        public static bool CheckMessageCRC(byte[] message, out string messageReceived)        {            //接收到的資料長度            var messageLength = message.Length;            //收到的資料資訊            var messageReceivedStrBytes = new byte[messageLength - 2];            Array.Copy(message, 0, messageReceivedStrBytes, 0, messageLength - 2);            //CRC校正碼固定2個位元組            var messageReceivedCrcBytes = new byte[2];            Array.Copy(message, messageLength - 2, messageReceivedCrcBytes, 0, 2);            //擷取傳輸資料            var messageCalculatedString = Encoding.Default.GetString(messageReceivedStrBytes);            messageReceived = messageCalculatedString;            //擷取CRC校正碼            var currentCRC = byte.Parse(messageReceivedCrcBytes[0].ToString()) * 256 + byte.Parse(messageReceivedCrcBytes[1].ToString());            //資料部分的crc校正            var result = ushort.Parse(CRC16.CalculateCrc16(messageCalculatedString));            if (currentCRC == result)                return true;            return false;        }

上面的過程就是對接收到的資料包,先把資料部分和CRC校正碼部分分開(校正碼部分很明確占最後兩個位元組,從CRC加驗部分我們就可以看到)。然後再把資料部分重新計算CRC校正碼,和附帶的對比即可。

後話

好了,以上就是所有的CRC校正碼內容了,包括加驗和校正過程,你明白了嗎?

說實在話,翻了許多文章,都沒有我這篇講得透徹。因為我在設計之初,也是找網上的文章,但是均不成功,後來和做硬體的人溝通,然後順利成型。

物聯網平台設計心得:你所不知道的CRC校正

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.