编辑
2025-09-17
C#
00

目录

什么是 CRC16?
基础实现方案
标准 CRC16 实现
使用示例
进阶实现:支持多种 CRC16 变体
多模式使用示例
实用工具方法
十六进制转换工具
注意
选择合适的 CRC16 变体
字节序问题
输入数据格式
总结

在通信和数据传输领域,数据完整性检验是一个必不可少的环节。CRC16 作为一种广泛应用的校验算法,以其高效可靠而受到开发者青睐。本文将从基础到进阶,详细讲解如何在 C# 中实现 CRC16 算法,帮助你掌握这一重要的数据校验技术。

什么是 CRC16?

CRC(循环冗余校验,Cyclic Redundancy Check)是一种错误检测码,用于验证数据在传输或存储过程中是否出现错误。CRC16 是其中使用 16 位宽度的一种实现,广泛应用于:

  • 工业通信协议(如 Modbus)
  • 嵌入式系统数据传输
  • 文件完整性校验
  • 网络数据包校验

基础实现方案

标准 CRC16 实现

下面是一个标准的 CRC16 实现,使用了查表法来提高计算效率:

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppCRC16 { public class CRC16 { // 标准 CRC16 多项式 private const ushort POLYNOMIAL = 0xA001; // 预计算的查找表 private readonly ushort[] table = new ushort[256]; public CRC16() { // 生成 CRC16 查找表 for (ushort i = 0; i < table.Length; ++i) { ushort value = 0; ushort temp = i; for (byte j = 0; j < 8; ++j) { if (((value ^ temp) & 0x0001) != 0) { value = (ushort)((value >> 1) ^ POLYNOMIAL); } else { value >>= 1; } temp >>= 1; } table[i] = value; } } public ushort ComputeChecksum(byte[] bytes) { ushort crc = 0; // 初始值 for (int i = 0; i < bytes.Length; ++i) { byte index = (byte)(crc ^ bytes[i]); crc = (ushort)((crc >> 8) ^ table[index]); } return crc; } } }

使用示例

C#
using System.Text; namespace AppCRC16 { internal class Program { static void Main(string[] args) { CRC16 crc16 = new CRC16(); // 示例1:计算字符串的 CRC16 string text = "Hello, World!"; byte[] bytes = Encoding.ASCII.GetBytes(text); ushort checksum = crc16.ComputeChecksum(bytes); Console.WriteLine($"CRC16 of '{text}': 0x{checksum:X4}"); // 示例2:计算字节数组的 CRC16 byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; checksum = crc16.ComputeChecksum(data); Console.WriteLine($"CRC16 of byte array: 0x{checksum:X4}"); Console.ReadLine(); } } }

image.png

进阶实现:支持多种 CRC16 变体

在实际应用中,不同的协议和系统可能使用不同的 CRC16 变体。下面是一个支持多种 CRC16 标准的实现:

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppCRC16 { internal class CRC16Calculator { public enum CRC16Mode { Standard, // 标准 CRC16 CCITT, // CRC16-CCITT Modbus // Modbus-CRC16 } private readonly ushort[] table; private readonly ushort polynomial; private readonly ushort initialValue; public CRC16Calculator(CRC16Mode mode = CRC16Mode.Standard) { switch (mode) { case CRC16Mode.CCITT: polynomial = 0x1021; initialValue = 0xFFFF; break; case CRC16Mode.Modbus: polynomial = 0xA001; initialValue = 0xFFFF; break; default: // Standard polynomial = 0xA001; initialValue = 0x0000; break; } table = new ushort[256]; GenerateTable(); } private void GenerateTable() { for (int i = 0; i < 256; i++) { ushort value = 0; ushort temp = (ushort)(i << 8); for (int j = 0; j < 8; j++) { if (((value ^ temp) & 0x8000) != 0) { value = (ushort)((value << 1) ^ polynomial); } else { value <<= 1; } temp <<= 1; } table[i] = value; } } public ushort Calculate(byte[] data) { ushort crc = initialValue; for (int i = 0; i < data.Length; i++) { byte index = (byte)((crc >> 8) ^ data[i]); crc = (ushort)((crc << 8) ^ table[index]); } return crc; } } }

多模式使用示例

C#
using System.Text; namespace AppCRC16 { internal class Program { static void Main(string[] args) { byte[] testData = Encoding.ASCII.GetBytes("Test Data"); // 使用标准 CRC16 var standardCrc = new CRC16Calculator(CRC16Calculator.CRC16Mode.Standard); ushort standardResult = standardCrc.Calculate(testData); Console.WriteLine($"Standard CRC16: 0x{standardResult:X4}"); // 使用 CCITT 变体 var ccittCrc = new CRC16Calculator(CRC16Calculator.CRC16Mode.CCITT); ushort ccittResult = ccittCrc.Calculate(testData); Console.WriteLine($"CCITT CRC16: 0x{ccittResult:X4}"); // 使用 Modbus 变体 var modbusCrc = new CRC16Calculator(CRC16Calculator.CRC16Mode.Modbus); ushort modbusResult = modbusCrc.Calculate(testData); Console.WriteLine($"Modbus CRC16: 0x{modbusResult:X4}"); Console.ReadLine(); } } }

image.png

实用工具方法

十六进制转换工具

在处理二进制数据时,经常需要在十六进制字符串和字节数组之间进行转换:

C#
using System.Text; namespace AppCRC16 { public static class CRC16Utilities { public static string ToHexString(byte[] bytes) { return BitConverter.ToString(bytes).Replace("-", ""); } public static byte[] HexStringToBytes(string hex) { if (string.IsNullOrEmpty(hex)) return new byte[0]; if (hex.Length % 2 == 1) hex = "0" + hex; byte[] result = new byte[hex.Length / 2]; for (int i = 0; i < result.Length; i++) { result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); } return result; } } internal class Program { static void Main(string[] args) { // 示例数据 string hexString = "48656C6C6F"; // "Hello" 的十六进制表示 // 将十六进制字符串转换为字节数组 byte[] data = CRC16Utilities.HexStringToBytes(hexString); // 计算 CRC16 var calculator = new CRC16Calculator(CRC16Calculator.CRC16Mode.Modbus); ushort crc = calculator.Calculate(data); // 输出结果 Console.WriteLine($"输入 (十六进制): {hexString}"); Console.WriteLine($"CRC16 校验值: 0x{crc:X4}"); // 验证数据完整性 byte[] dataWithCrc = new byte[data.Length + 2]; Array.Copy(data, dataWithCrc, data.Length); dataWithCrc[data.Length] = (byte)(crc & 0xFF); // 低字节 dataWithCrc[data.Length + 1] = (byte)(crc >> 8); // 高字节 Console.WriteLine($"带校验和的完整消息: {CRC16Utilities.ToHexString(dataWithCrc)}"); Console.ReadLine(); } } }

image.png

注意

选择合适的 CRC16 变体

不同的应用场景需要不同的 CRC16 实现,主要差异在于:

  • 多项式值(Polynomial)
  • 初始值(Initial Value)
  • 是否进行位反转
  • 最终结果是否异或

在实际应用中,需要确保使用的是正确的 CRC16 变体,否则会导致校验失败。

字节序问题

在某些协议中,CRC16 的高字节和低字节的顺序可能不同:

  • 低字节在前:[CRC_L, CRC_H]
  • 高字节在前:[CRC_H, CRC_L]

使用时需要注意按照协议规定的顺序处理字节。

输入数据格式

确保输入数据的格式正确,特别是在处理十六进制字符串时。常见错误包括:

  • 字符串长度为奇数
  • 包含非十六进制字符
  • 字节顺序错误

总结

本文详细介绍了 C# 中 CRC16 算法的实现方法,从基础的标准实现到支持多种变体的进阶实现,并提供了性能优化建议和实用工具方法。通过这些代码示例和说明,你应该能够在自己的项目中正确实现和使用 CRC16 算法,确保数据传输的完整性和可靠性。

无论是在工业通信、嵌入式系统还是网络应用中,CRC16 都是一种重要的数据校验技术。掌握它的实现原理和使用方法,将帮助你构建更加可靠的软件系统。

如果你在实际应用中遇到任何问题,欢迎在评论区留言讨论!


关键词:C#, CRC16, 循环冗余校验, 数据校验, Modbus, CCITT, 数据完整性, 工业通信, 校验算法, .NET编程

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!