【通信 - CRC检查】

什么是CRC

CRC是循环冗余校验(Cyclic Redundancy Check)的缩写。它是一种错误检测技术,通常用于检测数据传输或存储中的错误。CRC通过对数据进行多项式除法运算来生成一个校验码,发送方将这个校验码随数据一起发送给接收方,接收方再对接收到的数据进行相同的计算,并将结果与接收到的校验码进行比较,以确定数据是否在传输或存储过程中发生了错误。CRC广泛应用于计算机网络、存储设备以及数据通信等领域。

CRC应用领域

CRC(循环冗余校验)在各种计算机和通信系统中都有广泛的应用。以下是一些常见的CRC应用案例:

  1. 数据传输中的错误检测:CRC常用于数据传输中,特别是在通信协议中,以检测传输过程中引入的误码。例如,在网络通信、串行通信、无线通信等领域,数据通常通过CRC校验来检测传输中的错误。

  2. 存储介质中的数据完整性检查:在存储介质(如硬盘驱动器、闪存驱动器等)中,CRC被用于检测存储数据的完整性。当数据写入存储介质时,会计算CRC校验码并与数据一起存储。在读取数据时,会重新计算CRC并与存储的CRC进行比较,以检测数据是否损坏或被篡改。

  3. 网络协议中的帧校验:在许多网络协议(如以太网、Wi-Fi等)中,数据帧的完整性由CRC校验来保证。发送方会计算数据帧的CRC校验码,并将其附加到数据帧中。接收方在接收数据帧后会重新计算CRC,并将结果与接收到的CRC进行比较,以检测数据是否正确接收。

  4. 数字签名中的数据完整性验证:在数字签名中,CRC可以用作一种快速的数据完整性验证手段。发送方可以计算数据的CRC,并将其与数字签名一起发送。接收方在验证数字签名之前可以使用CRC来快速检查数据的完整性。

  5. Bootloader和固件更新:在嵌入式系统中,CRC常用于验证Bootloader或固件更新的完整性。设备在接收新的Bootloader或固件更新时会计算CRC,并与预期的CRC进行比较,以确保更新的完整性和正确性。

这些只是CRC的一些常见应用案例,它在各种领域中都有重要作用,确保数据的完整性和可靠性。

CRC实现方法

CRC(循环冗余校验)有许多不同的实现方法,每种方法都有自己的优缺点,适用于不同的应用场景。以下是一些常见的CRC实现方法以及相应的C代码示例:

1. 位级方法(Bitwise Method):

直接在比特级别上执行CRC计算,通常用于较低端的嵌入式系统或要求较低的资源消耗。

#include <stdio.h>
#include <stdint.h>
/* 这个数值对应于CRC-32算法中的标准多项式。
 * CRC-32是一种广泛使用的CRC算法,它使用32位多项式进行计算。
 * 在CRC-32中,使用的多项式是0x04C11DB7,但在二进制反转之后,它的值变为0xEDB88320。
 * 这个特定的多项式经过了严格的数学分析和实验验证,被证明在CRC算法中表现良好。
 * 它具有较好的差错检测性能和较低的碰撞概率,因此被广泛应用于各种通信和存储系统中。
 * 在C代码中,将CRC_POLYNOMIAL定义为0xEDB88320使得代码更易读和理解,
 * 因为它直接反映了所使用的CRC多项式的值。
 * */
#define CRC_POLYNOMIAL 0xEDB88320

/* 计算CRC的位级方法 */
uint32_t calculate_crc_bitwise(const uint8_t *data, size_t length) {
    uint32_t crc = 0xFFFFFFFF; // 初始化CRC值为全1
    size_t i, j;
/* 对数据的每个字节进行处理 */
    for (i = 0; i < length; i++) {
        crc ^= data[i]; // 用数据字节异或当前CRC值
        /* 对每个字节的8个比特位进行处理 */
        for (j = 0; j < 8; j++) {
	        /* 根据CRC多项式执行异或运算 */
            crc = (crc >> 1) ^ ((crc & 1) ? CRC_POLYNOMIAL : 0);
        }
    }
	/* 返回CRC的反码作为最终的CRC值 */
    return ~crc;
}

int main() {
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    size_t length = sizeof(data) / sizeof(data[0]);
    uint32_t crc = calculate_crc_bitwise(data, length);
    printf("CRC: 0x%08X\n", crc);
    return 0;
}

2. 表驱动方法(Table-Driven Method):

使用预先计算好的CRC表来执行CRC计算,可以提高计算速度,但需要额外的内存来存储CRC表。

#include <stdio.h>
#include <stdint.h>

#define CRC_TABLE_SIZE 256
#define CRC_POLYNOMIAL 0xEDB88320

/* CRC表 */
uint32_t crc_table[CRC_TABLE_SIZE];
/* 初始化CRC表 */
void init_crc_table() {
    uint32_t crc;
    int i, j;

    for (i = 0; i < CRC_TABLE_SIZE; i++) {
        crc = i;
        for (j = 0; j < 8; j++) {
        	/* 计算CRC表项值 */
            crc = (crc >> 1) ^ ((crc & 1) ? CRC_POLYNOMIAL : 0);
        }
        /* 将计算得到的CRC值存入CRC表中 */
        crc_table[i] = crc;
    }
}
/* 使用表驱动方法计算CRC */
uint32_t calculate_crc_table(const uint8_t *data, size_t length) {
	/* 初始化CRC值为全1 */
    uint32_t crc = 0xFFFFFFFF;
    size_t i;

    for (i = 0; i < length; i++) {
    /* 查表计算CRC值
     * 1. `crc >> 8`:这一步将当前CRC值右移8位,即将CRC值向右移动一个字节,为了便于处理下一个数据字节。
     * 2. `crc_table[(crc ^ data[i]) & 0xFF]`:这一部分是在CRC表中查找新的CRC值。具体解释如下:
     * 	  - `crc ^ data[i]`:这一步将当前CRC值与数据字节进行异或操作。这样做是为了将当前数据字节与CRC值组合起来,以产生一个新的32位数值。
     *    - `& 0xFF`:这一步将结果限制在0xFF范围内,即只保留结果的低8位,因为CRC表通常只有256个条目。
     *    - `crc_table[...]`:最后,根据结果在CRC表中查找对应的CRC值。
     * 3. `^`:这一步是将右移8位后的CRC值和查找到的新CRC值进行异或操作。这是因为CRC的计算过程中,除了与数据进行异或操作外,还需要与之前的CRC值进行异或操作,以确保新的数据字节被正确地合并到CRC值中。
     * 这一行代码实现了CRC表驱动方法中的核心步骤,通过查表的方式快速获取新的CRC值,并将其与之前的CRC值进行异或,从而实现了数据的逐字节处理和CRC值的更新。
     * */
        crc = (crc >> 8) ^ crc_table[(crc ^ data[i]) & 0xFF];
    }
	 /* 返回CRC的反码作为最终的CRC值 */
    return ~crc;
}

int main() {
    init_crc_table();
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    size_t length = sizeof(data) / sizeof(data[0]);
    uint32_t crc = calculate_crc_table(data, length);
    printf("CRC: 0x%08X\n", crc);
    return 0;
}

3. 快速多项式乘法方法(Fast Polynomial Multiplication Method):

将CRC计算表示为多项式乘法运算,可以应用快速多项式乘法算法来提高计算效率。

#include <stdio.h>
#include <stdint.h>
/* CRC-32算法的多项式 */
#define CRC_POLYNOMIAL 0xEDB88320

/* 快速多项式乘法方法,用于计算两个32位整数a和b的乘积。
 * 这个函数实现了一种快速的多项式乘法算法,利用了位操作的特性,通过移位和异或运算来实现乘法操作。
 * 这种算法的时间复杂度为O(n),其中n是b的位数,因此它是一种高效的算法。*/
uint32_t polynomial_multiply(uint32_t a, uint32_t b) {
    uint32_t result = 0;
    /* 当第二个操作数b不为0时,执行循环,确保对每个b的位进行处理。 */
    while (b) {
    /* 检查b的最低位是否为1。如果是,说明a需要与结果进行异或运算。
     * 这是因为多项式乘法中,b的最低位为1时,a需要加到结果中。 */
        if (b & 1)
            result ^= a;
    /* 将a左移一位,相当于将a乘以2;
     * 将b右移一位,相当于将b除以2。
     * 这是因为在多项式乘法中,b的每一位对应的是多项式的一个系数,右移相当于去掉最低位的系数。 */
        a <<= 1;
        b >>= 1;
    }
    return result;
}
/* 计算数据的CRC校验码(多项式乘法方法)*/
uint32_t calculate_crc_polynomial(const uint8_t *data, size_t length) {
    uint32_t crc = 0xFFFFFFFF;
    size_t i;

    for (i = 0; i < length; i++) {
        crc ^= data[i]; // 将数据字节与当前CRC值进行异或
        crc = polynomial_multiply(crc, CRC_POLYNOMIAL); // 使用快速多项式乘法计算新的CRC值
    }

    return ~crc;// 返回CRC的反码作为最终的CRC值
}

int main() {
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    size_t length = sizeof(data) / sizeof(data[0]);
    uint32_t crc = calculate_crc_polynomial(data, length);
    printf("CRC: 0x%08X\n", crc);
    return 0;
}

CRC 与 简单求和方法 对比

CRC方法与简单计算各字节之和之间存在一些重要区别,它们适用于不同的情况,具有不同的优缺点。

CRC方法的优缺点:

优点:

  1. 更可靠的错误检测:CRC能够检测到更多类型的错误,包括更复杂的传输错误,而简单的字节求和方法可能会漏掉某些错误。
  2. 更高的数据完整性:CRC通常能够提供更高的数据完整性,因为它具有更低的碰撞概率,可以检测到更多的数据损坏情况。
  3. 检测能力更强:CRC通常能够检测出更小的数据变化,因此在对数据完整性要求较高的场景中更适用。

缺点:

  1. 计算复杂度较高:CRC计算通常涉及更复杂的算法和运算,因此它的计算开销比简单的字节求和方法要大。
  2. 算法实现复杂:实现CRC算法需要一定的编程经验和理解,相比之下,简单的字节求和方法更易于实现和理解。

简单求和方法的优缺点:

优点:

  1. 计算速度快:简单求和方法的计算速度较快,因为它只涉及简单的加法运算,不需要复杂的算法。
  2. 实现简单:这种方法的实现非常简单直观,不需要复杂的逻辑和数据结构。

缺点:

  1. 容错能力低:简单求和方法容易受到特定类型的错误干扰,可能会漏掉某些错误。
  2. 数据完整性较低:由于它的容错能力较低,因此简单求和方法提供的数据完整性可能不如CRC方法。

总的来说,如果数据的完整性和可靠性至关重要,或者需要在不同的环境中传输数据,CRC方法通常是更好的选择。但如果对数据完整性要求较低,或者在资源受限的环境中,简单的求和方法可能更适合。

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六月悉茗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值