参考1: https://blog.****.net/d_leo/article/details/73572373
参考2:https://wenku.baidu.com/view/856d4f584b35eefdc9d3333f.html?from_page=view&from_mod=download
CRC即循环冗余校验码(Cyclic Redundancy Check):其特征是信息字段和校验字段的长度可以任意选定。在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码也叫(N,K)码。对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。多项式是R+1位,且多项式G(x)首位和最后一位都为1。根据K位信息码和G(x)可以生成R位信息的校验码。
R位校验码的具体生成过程为:假设要发送的K位信息码用多项式C(X)表示,将C(x)左移R位(可表示成C(x)*2R),这样C(x)的右边就会空出R位,这就是校验码的位置。用 C(x)*2R再模2除 (注意,这里不是直接采用二进制除法,而是采用一种称之为“模2除法”)。以生成多项式G(x)得到的余数就是R位校验码。
R+1位多项式:任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和‘1’取值的多项式一一对应。例如:代码1010111对应的多项式为x6+x4+x2+x+1,而多项式为x5+x3+x2+x+1对应的代码101111。
模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。
例:
现假设选择的CRC生成多项式为G(X) = X4 + X3 + 1,要求出二进制序列10110011的CRC校验码。下面是具体的计算过程:
①将多项式转化为二进制序列,由G(X) = X4 + X3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001
②多项式的位数位5,则在数据帧的后面加上5-1位0,数据帧变为101100110000,然后使用模2除法除以除数11001,得到余数。
③将计算出来的CRC校验码添加在原始帧的后面,真正的数据帧为101100110100,再把这个数据帧发送到接收端。
④接收端收到数据帧后,用上面选定的除数,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。
我么假设发送信息为16位,生成多项式采用17位的G(x)0x11021,得到16位的CRC校验码。信息码+校验码共32位
发送端进行CRC校验合成代码如下:
#include<stdio.h>
#define _CRT_SECIRE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
int crc;
int crc_CCITT_Send(int data_crc)
{
int data = 0, ax = 0, bx = 0, cx = 0;
int px = 0x11021; //CRC_CCITT的生成多项式
data = data_crc; //data_crc为信息内容
data <<= 16; //信息左移16位
ax = data >> 15; //将前17位存在ax中,准备与多项式px做异或操作
data <<= 17; //除了前17位后剩下的15位
for (cx = 15; cx > 0; cx--)
{
if (((ax >> 16) & 0x1) == 0x1)
{
ax = ax ^ px;
}
ax <<= 1;
bx = data >> 31;
ax = ax + bx;
data <<= 1;
}
if (((ax >> 16) & 0x1) == 0x1) //最后一位的异或操作
{
ax = ax ^ px;
}
crc = ax; //crc 为根据信息内容data_crc和CRC_CCITT的生成多项式px求出的CRC码。
printf("\n\n原始信息:%x\n", data_crc);
data_crc <<= 16;
data_crc += crc;
printf("多项式信息:%x\n", px);
printf("生成CRC校验码:%x\n", crc);
printf("最后生成信息:%x\n", data_crc);
return data_crc;
}
int main()
{
FILE *fp_data, *fp_code;
int crcnum, ch;
fp_code = fopen("./code.txt", "w"); //加密文档
fp_data = fopen("./data.txt", "w"); //原始文档
printf("*****************************************************\n");
printf("请输入原始信息,输入-1退出:");
while (1)
{
scanf("%x", &ch);
if (ch == -1)
{
break;
}
crcnum = crc_CCITT_Send(ch); //求出经过CRC校验后加密信息
fprintf(fp_code, "%x ", crcnum);
fprintf(fp_data, "%x ", ch);
}
printf("*****************************************************\n");
fclose(fp_code);
fclose(fp_data);
system("pause");
return 0;
}
接收端代码如下:
#include<stdio.h>
#define _CRT_SECIRE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
int crc_CCITT_Recv(int data_crc)
{
int data = 0, ax = 0, bx = 0, cx = 0;
int px = 0x11021; //CRC_CCITT的生成多项式
data = data_crc; //data_crc为信息内容
data <<= 16;
ax = data >> 15;
data <<= 17;
for (cx = 15; cx > 0; cx--)
{
if (((ax >> 16) & 0x1) == 0x1)
{
ax = ax ^ px;
}
ax <<= 1;
bx = data >> 31;
ax = ax + bx;
data <<= 1;
}
if (((ax >> 16) & 0x1) == 0x1)
{
ax = ax ^ px;
}
printf("\n\n原始信息:%x\n", data_crc);
printf("多项式信息:%x\n", px);
printf("最后模2除结果:%x\n", ax);
return ax; //ax 加密信息data_crc模2除多项式px的余数
}
int main()
{
FILE *fp_code;
unsigned int data_crc, crcResult, ch, nLen=0;
unsigned int n = 16;
fp_code = fopen("./code.txt", "r"); //加密文档
fseek(fp_code, 0, SEEK_END); //文件指针移到文件尾
nLen = ftell(fp_code); //得到当前指针位置, 即是文件的长度
printf("文件长度为%d\n", nLen);
if (nLen <= 0)
{
printf("文件错误!\n");
exit(1);
}
fseek(fp_code, 0, SEEK_SET); //文件指针移到文件开头
printf("*****************************************************\n");
printf("读取加密文件信息:");
while (fscanf(fp_code, "%x", &ch) != EOF)
{
printf("读出数据为:%x\n", ch);
data_crc = ch >> n;
crcResult = crc_CCITT_Recv(data_crc);
ch <<= n;
ch >>= n;
if ((ch ^crcResult) != 0)
{
printf("传输错误!\n");
exit(2);
}
}
printf("\n\n\n文件传输成功!\n");
printf("*****************************************************\n");
fclose(fp_code);
;
system("pause");
return 0;
}