今天簡單介紹一些傳輸數據校驗的方法,就昨天整理的資料和就我的理解寫的Demo做個總結!希望大家多多指教!
定義
通俗的說,就是為保證數據的完整性,用一種指定的算法對原始數據計算出的一個校驗值。接收方用同樣的算法計算一次校驗值,如果和隨數據提供的校驗值一樣,說明數據是完整的。
實際應用
校驗方法
數據校驗無非就是三個步驟:一、添加校驗碼;二、校驗數據;三、還原數據。 如下圖:(具體方法后面一一介紹)
先看看這次咱們學習哪些,我在里面定義的一個枚舉
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 (下面介紹來自百度百科)
簡單例子:
奇校驗(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)
/// <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校驗和數字簽名
/// <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();
運行結果: