一、设计目标
1)环境要求:Windows; 2)信息交换内容为文本文件;
3)通信信息交换通过共享文件实现编码要求:用模 2 除法计算 CRC 码,生成多项式为 CRC-16
4)功能要求:能在两台计算机机上运行程序,一台产生 CRC 码,另一台校验。
二、设计原理和方法
1、CRC简介及原理:
CRC码为循环冗余校验码,基本表示方式为(n,k),其中n为数据位数,k为校验码位数。CRC码校验的基本思想是利用线性编码理论,在发送端根据要传送的(n,k)位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。采用CRC校验时,发送方和接收方用同一个生成多项式g(x),并且g(x)的首位和最后一位的系数必须为1。
CRC的处理方法是:发送方以g(x)去除t(x),得到余数作为CRC校验码。校验时,以计算的校正结果是否为0为据,判断数据帧是否出错。CRC校验可以100%地检测出所有奇数个随机错误和长度小于等于k(k为g(x)的阶数)的突发错误。所以CRC的生成多项式的阶数越高,那么误判的概率就越小。CCITT建议:2048 kbit/s的PCM基群设备采用CRC-4方案,使用的CRC校验采用16位CRC校验。在IBM的同步数据链路控制规程SDLC的帧校验序列FCS中,使用CRC-16。CRC的本质是模-2除法的余数,采用的除数不同,CRC的类型也就不一样。通常,CRC的除数用生成多项式来表示。最常用的CRC码的生成多项式有CRC16,CRC32.
16位CRC码的产生的规则是先将要发送的二进制序列数左移32位后,再除以一个多项式(生成多项式G(x)),最后得到的余数既是CRC码,如式(2-1)式所示,其中C(X)表示(n-k)位的二进制序列数,G(X)为多项式,Q(X)为商(整数),R(X)是余数(既CRC码)。
Cx216Rx QxGxGx 求CRC码所采用模2加减运算法则,既是不带进位和借位的按位加减,这种加减运算实际
上就是逻辑上的异或运算,加法和减法等价,乘法和除法运算与普通代数式的乘除法运算是一样,符合同样的规律。
接收方将接收到的二进制序列数(包括信息码和CRC码)除以多项式,如果余数为0,则说明传输中无错误发生,否则说明传输有误。用软件计算CRC码时,接收方可以根据接收到的信息码求CRC码,比较结果和接收到的CRC码是否相同。
例如信息位是“1111”、多项式g(x)=1001,用模二除法生成CRC码的过程如下所示: ①在信息位“1111”后面增加3个“0”,即“1111000” ②用“1111000”模二除g(x)=1001
③得到的余数“110”即为“1111”的CRC校验码;将其加在原数据后面,即“1111110”,通过发送端发送,在接收端收到数据再除以多项式g(x),若余数为0,则传输正确,去掉后三位即得到需要的数据“1111”;若信道有干扰,使接收到的数据不是“1111110”,除以多项式g(x)后余数不为0,则传输失败,等待重传。
2、CRC校验和计算法
将待发送的位串看成系数为 0 或 1 的多项式;收发双方约定一个生成多项式 G(x)(其最高阶和最低阶系数必须为1),发送方用位串及 G(x)进行某种运算得到校验和,并在帧的末尾加上校验和,使带校验和的帧的多项式能被 G(x) 整除; 接收方收到后,用 G(x) 除多项式,若有余数,则传输有错。
用计算机编程实现CRC校验码,采用寄存器移位的方法。
假设待测数据是1101 0110 11,生成项是10011,假设有一个4 bits的寄存器,通过反复的移位和进行CRC的除法,最终该寄存器中的值就是我们所要求的余数。 3 2 1 0 Bits +---+---+---+---+
Pop <-- | | | | | <----- Augmented message(已加0扩张的原始数据) +---+---+---+---+
1 0 0 1 1 = The Poly 生成项
3、差错检测
(1)直接用接收到的序列除以生成多项式G(x),如果余数R’(x) = 0,则证明传输正确。 (2)提取接收到序列的信息码元,重复发送方的操作xrM(x) ,再除以生成多项式G(x),如果余数R’(x) = R(x),则证明传输正确。
这里还有个问题,如果被除数比除数小,那么余数就是被除数本身,比如说只要传一个字节,那么它的CRC就是它自己,为避免这种情况,在做除法之前先将它移位,使它大于除数,那么移多少位呢?这就与所选的固定除数有关了,左移位数比除数的位数少1,下面是常用标准中的除数:
CRC8:多项式是X8+X5+X4+1,对应的数字是0x131,左移8位。 CRC12:多项式是X12+X11+X3+X2+1,对应的数字是0x180D,左移12位。 CCITT CRC16:多项式是X16+X12+X5+1,对应的数字是0x11021,左移16位。 ANSI CRC16:多项式是X16+X15+X2+1,对应的数字是0x18005,左移16位。 CRC32:多项式是X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1,对应数字是0x104C11DB7,左移32位。
4、任意位数信息码的CRC16计算
当我们求得一个字节(比如0x88)的CRC校验码后,如果这时又有一个字节(比如0x23)加入,需要求校验码,该怎么办呢?以前得到的是0x880000除以0x11021的余数,但现在需要得到的是0x88230000除以0x11021的余数,以前求得的校验码是不是白费了?不是的,因为当我们得到0x880000除以0x11021的余数(这里余数是0x1080)后,需要求0x88230000除以0x11021的余数,只要将原来的余数0x1080左移8位除以0x11021就得到了0x88000000除以0x18005的余数,再加上0x230000除以0x11021的余数。这其实就是求
0x108000+0x230000除以0x11021的余数.。因此根据这个方法,我们就可以写出求多个字节的CRC算法:
5、实现信息交换和传输需要解决的问题
① 设计要求信息交换内容为文本文件,即在相应目录下建立文本文件code.txt和crc.txt,分别用来存放数据code和数据crc;
② 设计要求通信传输方式通过共享文件实现,即在电脑A上将文本文件code.txt和crc.txt放在一个文件夹里例如建立文件夹,将liqiuju文件夹设为共享,点击liqiuju文件——右键——属性/共享,进行设置。
在电脑B上通过网上邻居找到电脑A地址,进入找到文件夹liqiuju下的code.txt文件路径,在接收端程序里面设置相应的路径。
三、设计的功能
1)通信信息交换通过共享文件实现编码要求:用模 2 除法计算 CRC 码,生成多项式为 CRC-16
2)功能要求:能在两台计算机机上运行程序,一台产生 CRC 码,另一台校验。
编程实现最简单的定长16进制数的CRC16计算,将循环冗余校验码放到信息码后面,完成发送端编码;然后对编码进行校验,以相同的方式计算余数,若余数为0,则完成了最基本的功能。完成定长之后,逐步拓展为多位长度计算和变长计算,最后尝试接收端的解码。
四、程序框图
发送端(电脑A) ---------文件共享---------- 接收端(电脑B) 开始 待编码内容 转换为16进制码 CRC-16码计算 CRC 码写到信息码后 保存到文本 文件 结束
五、程序清单
1.发送端电脑A源程序:#include #include 开始 读取文本内容 计算CRC码 N CRC=0 ? Y 解码其中的信息文件错码 误 发送的内容 结束 #include unsigned short crc16_ccitt(unsigned char data, unsigned short crc)//算校验码的函数 { unsigned short ccitt16 = 0x1021; int i; crc ^= (data << 8); /* 新的数据与将原来的余数(就是crc)相加(加法就是异或 操作) */ /* 求数据的CRC校验码 */ for (i = 0; i<8; i++) { if (crc & 0x8000) /* 按位与判断最高位为1,则减去除数 */ { crc <<= 1; /* 将最高位的1移出,剩下的数与0x1021(而不是0x11021) 异或,这是因为最高位的1与0x11021的最高位1异或后为0 */ } } return crc; } else /* 最高位为0,不需要减去除数 */ { } crc <<= 1; /* 直接移位 */ crc ^= ccitt16; int main(void) { string str; //字符串充当变长数组 cout<<\"请输入文件的内容\"< int m,n; //m为长度 m=str.size();//求字符串长度 int i; i=0; int data[m];//待处理的数据码元 int aaa=0; cout< while(i 有符号位,因此能表示0~255,8个bit,最多256种情况,因此无论如何都能表示256个数字。) data[i]=(unsigned char)str[i]; i=i+1;//将每一个字符换成16进制表示 } cout< for (i = 0; i //crc码为4位16进制数,但是计算crc的结果并不会补位,如果计算结果不满四位,需要手动添加0补位。 if(crc<16) { } else if(crc<256) { } else if(crc<4096) { cout<<\"c\"; cout< cout <<\"crc: \"< } cout << \"code is \"; int shuchu_i = 0; for (shuchu_i = 0; shuchu_i < m; shuchu_i++)//m为长度 { cout < else if(crc<256) { cout< else if(crc<4095) { cout< cout << hex << crc << endl << endl; char* path = \"E:\\\\CRC.txt\"; // 你要创建文件的路径 ofstream fout(path); if (fout) { // 如果创建成功 int xieru_i = 0; for (xieru_i = 0; xieru_i < m; xieru_i++)//写入 { fout << hex << data[xieru_i]; } if(crc<16) { fout< else if(crc<256) { fout< else if(crc<4096) { fout << hex < fout << hex << crc << endl; // 使用与cout同样的方式进行写入 fout.close(); // 执行完操作后关闭文件句柄 cout << \"写入成功\" << endl; return 0; } 2.接收端电脑B源程序: #include #include unsigned short crc16_ccitt(unsigned char data, unsigned short crc) { unsigned short ccitt16 = 0x1021; int i; crc ^= (data << 8); /* 新的数据与将原来的余数(就是crc)相加(加法就是异或 操作) */ /* 求数据的CRC校验码 */ for (i = 0; i<8; i++) { if (crc & 0x8000) /* 最高位为1,减去除数 */ { crc <<= 1; /* 将最高位的1移出,剩下的数与0x1021(而不是0x11021) 异或,这是因为最高位的1与0x11021的最高位1异或后为0 */ } int main(void) { ifstream fin(\"E:\\\\CRC.txt\"); //从文件读数据(从文件读入) string s; fin >>hex>> s ; cout< int m,n;//m为长度 m=s.size();//求字符串长度 int i=0; int data[m/2]; char messages[m/2]; int code_length=m/2-2; n=0; } return crc; } else /* 最高位为0,不需要减去除数 */ { } crc <<= 1; /* 直接移位 */ crc ^= ccitt16; while(i int ss = static_cast data[n]=ss; // message[n]=ss; n=n+1; i=i+2; } unsigned short crc; // /* 计算这5个数据的CRC校验码 */ if(!crc) { cout<<\"文件正确\"< i=0; cout<<\"文件内容: \"; while(i i=i+2; } cout< 6.1文字编码 发送端数据 6.2 读取共享文件 验证及解码 6.3 错误文件检测 当我手动更改CRC.txt文档中存储的数据后: 因篇幅问题不能全部显示,请点此查看更多更全内容string a,b,c;