CRC使用java实现循环冗余检测 ,计网课程实验
一、CRC生成算法原理
1.1 多项式编码 多项式编码(polynomial code),也称为CRC(cyclic redundancy check,循环冗余校验码),多项式编码的思想是:将位串看成是系数为0或1的多项式。CRC校验保护的单位是数据块。数据块的大小根据实际情况而定。每一个数据块均被看作是一个二进制多项式,即所有系数均为二进制(即1或0)的多项式。
当使用多项式编码时,发送方和接受方必须预先商定一个生成多项式(generator polynomial)G(x)。生成多项式的最高位和最低位必须为1。
1.2 CRC原理 将待发送的位串看成系数为 0 或 1 的多项式 收发双方约定一个生成多项式 G(x) (其最高阶和最低阶系数必须为1),发送方用位串及 G(x)进行某种运算得到校验和,并在帧的末尾加上校验和,使带校验和的帧的多项式能被 G(x) 整除; 接收方收到后,用 G(x) 除多项式,若有余数,则传输有错。
1.3 CRC校验和计算法
1.3.1 若生成多项式 G(x) 为 r 阶(即r+1位位串),原帧为 m 位, 其多项式为 M(x),则在原帧后面添加 r 个 0,即循环左移r位,帧成为 m+r 位,相应多项式成为 xrM(x)xrM(x);
1.3.2按模2除法用 G(x)对应的位串去除对应于xrM(x)xrM(x) 的位串, 得余数 R(x);
1.3.3 按模2减法(即模2加)从对应于 xrM(x)xrM(x)的位串中减去(加上)余数 R(x),结果即传送的带校验和的帧多项式T(x)。 T(x)=xrM(x)+R(x) T(x)=xrM(x)+R(x)
1.3.4 例子
1.4 CRC冗余检测源码,有java窗体版本
这里使用的是字符串判断实现,没有长度限制,第一次发csdn,各位路过的大佬们轻点,你们的支持就是我最大的动力,感谢!!!
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author czy
* @create 2021-11-24 8:33
*/
public class CRCFrame extends JFrame {
private JLabel lbl_1 = new JLabel("请输入二进制字符:");
private JLabel lbl_2 = new JLabel("请输入除数:");
private JLabel lbl_3 = new JLabel("加零后的被除数:");
private JLabel lbl_4 = new JLabel("冗余码:");
private JLabel lbl_5 = new JLabel("加冗余码之后的被除数:");
private JLabel lbl_6 = new JLabel("验算结果:");
private JLabel lbl_7 = new JLabel("输入需要转化为二进制的字符:");
private JTextField field_字符串 = new JTextField();
private JTextField field_二进制 = new JTextField();
private JTextField field_除数 = new JTextField();
private JTextField field_加零被除数 = new JTextField();
private JTextField field_冗余码 = new JTextField();
private JTextField field_加冗余码的被除数 = new JTextField();
private JTextField field_结果 = new JTextField();
private JButton btn_start = new JButton("开始计算");
private JButton btn_start_result = new JButton("开始验算");
private JButton btn_start_turn = new JButton("转化为二进制");
private JButton btn_clear = new JButton("清空所有");
/**
* 计算过程面板,注释掉然后吧所有的panel去掉即可,
* 其他地方要改就再自己改改
*/
private JPanel panel = new JPanel();
private int count = 0;
public CRCFrame() {
this.add(lbl_1);
this.add(lbl_2);
this.add(lbl_3);
this.add(lbl_4);
this.add(lbl_5);
this.add(lbl_6);
this.add(lbl_7);
this.add(field_字符串);
this.add(field_二进制);
this.add(field_除数);
this.add(field_加零被除数);
this.add(field_冗余码);
this.add(field_加冗余码的被除数);
this.add(field_结果);
this.add(btn_start_turn);
this.add(btn_start);
this.add(btn_start_result);
this.add(btn_clear);
this.add(panel);
field_字符串.setBounds(300, 40, 300, 40);
int initx = 150;
int inity = 140;
int initxx = 300;
int inityy = 140;
lbl_7.setBounds(initx - 30, inity - 100, 200, 40);
lbl_1.setBounds(initx, inity, 200, 40);
lbl_2.setBounds(initx, inity + 100, 200, 40);
lbl_3.setBounds(initx, inity + 200, 200, 40);
lbl_4.setBounds(initx, inity + 300, 200, 40);
lbl_5.setBounds(initx, inity + 400, 200, 40);
lbl_6.setBounds(initx, inity + 500, 200, 40);
field_二进制.setBounds(initxx, inityy, 300, 40);
field_除数.setBounds(initxx, inityy + 100, 300, 40);
field_加零被除数.setBounds(initxx, inityy + 200, 300, 40);
field_冗余码.setBounds(initxx, inityy + 300, 300, 40);
field_加冗余码的被除数.setBounds(initxx, inityy + 400, 300, 40);
field_结果.setBounds(initxx, inityy + 500, 300, 40);
btn_start_turn.setBounds(700, 100, 200, 40);
btn_start.setBounds(700, 300, 100, 40);
btn_start_result.setBounds(700, 540, 100, 40);
btn_clear.setBounds(700, 640, 100, 40);
panel.setBounds(1000, 0, 700, 1000);
field_二进制.setText("101001");
field_除数.setText("1101");
//绑定单击事件
btn_start.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
count = 0;
panel = new JPanel();
CRCFrame.this.add(panel);
panel.setBounds(1000, 0, 700, 1000);
//开始计算结果
getResult();
}
});
btn_start_result.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//开始验算
getIsNotError();
}
});
btn_start_turn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String s = translateToBinary(field_字符串.getText());
field_二进制.setText(s);
}
});
btn_clear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
clearContents();
}
});
}
/**
* 冗余检测,求出冗余码
*/
public void getResult() {
System.out.println("开始计算结果");
//获取除数
String 被除数 = field_二进制.getText().trim();
String 除数 = field_除数.getText().trim();
for (int i = 0; i < 除数.length() - 1; i++) {
被除数 += 0;
}
field_加零被除数.setText(被除数);
showTitle();
//101001000
//1101
System.out.println(field_加零被除数.getText());
int current = 0;
int str_length = 被除数.length();
while (current + 除数.length() != str_length + 1) {
除数 = field_除数.getText().trim();
//获取当前需要进行计算的字符串
String current_str = 被除数.substring(0, 除数.length());
//获取剩余的字符串
String current_str_residue = 被除数.substring(除数.length());
//记录每一次进行模二运算的结果
String moer = "";
for (int i = 0; i < current_str.length(); i++) {
//被除数被选中计算的第一位数大于等于除数,则需要用于计算的是 原除数 ,否则是除数 位数个0,也就是商能不能取1
if (Integer.parseInt(current_str.substring(0, 1)) < Integer.parseInt(除数.substring(0, 1))) {
int length = 除数.length();
除数 = "";
for (int i1 = 0; i1 < length; i1++) {
//商不能取1之后设置除数
除数 += 0;
}
}
String 除数_s = 除数.substring(i, i + 1);
String current_s = current_str.substring(i, i + 1);
//如果相等
if (current_s.equals(除数_s)) {
moer += 0;
//不相等
} else {
moer += 1;
}
}
//输出当前需要进行模二运算的字符
System.out.println(current_str);
System.out.println(除数);
System.out.println("====");
showDetail(current_str, 除数);
//取出n-1位模运算的结果
被除数 = moer.substring(1) + current_str_residue;
//输出每次的余数
System.out.println(被除数);
current++;
}
field_冗余码.setText(被除数);
showDetail("", 被除数);
field_加冗余码的被除数.setText(field_二进制.getText() + field_冗余码.getText());
}
/**
* 将字符串中的字符逐个读取出来转化为二进制再拼接
*
* @param string
* @return
*/
public String translateToBinary(String string) {
char[] strChar = string.toCharArray();
String result = "";
for (int i = 0; i < strChar.length; i++) {
result += Integer.toBinaryString(strChar[i]) + "";
}
System.out.println(string + "转换为二进制是:\n" + result);
return result;
}
/**
* 验算
*
* @return
*/
public boolean getIsNotError() {
System.out.println("\n\n\n开始验算");
String 被除数 = field_加冗余码的被除数.getText();
String 除数 = field_除数.getText();
System.out.println(被除数);
int current = 0;
int str_length = 被除数.length();
while (current + 除数.length() != str_length + 1) {
除数 = field_除数.getText();
//获取当前需要进行计算的字符串
String current_str = 被除数.substring(0, 除数.length());
//获取剩余的字符串
String current_str_residue = 被除数.substring(除数.length());
//记录每一次进行模二运算的结果
String moer = "";
for (int i = 0; i < current_str.length(); i++) {
//被除数被选中计算的第一位数大于等于除数,则需要用于计算的是 原除数 ,否则是除数 位数个0,也就是商能不能取1
if (Integer.parseInt(current_str.substring(0, 1)) < Integer.parseInt(除数.substring(0, 1))) {
int length = 除数.length();
除数 = "";
for (int i1 = 0; i1 < length; i1++) {
//商不能取1之后设置除数
除数 += 0;
}
}
String 除数_s = 除数.substring(i, i + 1);
String current_s = current_str.substring(i, i + 1);
//如果相等
if (current_s.equals(除数_s)) {
moer += 0;
//不相等
} else {
moer += 1;
}
}
//输出当前需要进行模二运算的字符
System.out.println(current_str);
System.out.println(除数);
System.out.println("====");
//取出n-1位模运算的结果
被除数 = moer.substring(1) + current_str_residue;
//输出每次的余数
System.out.println(被除数);
current++;
}
field_结果.setText(被除数);
return false;
}
/**
* 清空所有
*/
public void clearContents() {
field_字符串.setText("");
field_二进制.setText("");
field_除数.setText("");
field_加零被除数.setText("");
field_冗余码.setText("");
field_加冗余码的被除数.setText("");
field_结果.setText("");
field_二进制.setText("");
field_除数.setText("");
}
public void showTitle() {
JLabel lab = new JLabel("计算过程如下:");
JLabel label = new JLabel(field_除数.getText() + " ) " + field_加零被除数.getText());
panel.add(lab);
panel.add(label);
label.setBounds(50, 100, 400, 20);
lab.setBounds(50, 70, 400, 20);
}
/**
* 页面显示详细的计算过程
*/
public void showDetail(String out, String 除数) {
System.out.println("");
String null_str = "^^^^^ ";
for (int i = 0; i < count; i++) {
null_str += "^";
}
JLabel label = new JLabel(null_str + out);
JLabel label_1 = new JLabel(null_str + 除数);
JLabel label_line = new JLabel("____________________________________");
panel.add(label);
panel.add(label_1);
panel.add(label_line);
label.setBounds(50, 120 + count * 60, 400, 20);
label_1.setBounds(50, 140 + count * 60, 400, 20);
label_line.setBounds(50, 160 + count * 60, 400, 20);
count++;
}
}
**
代码运行结果
太长的二进制计算会导致计算过程显示不全
如果输入的字符较短时可以得出具体的过程
注意
上面的源码是完整的窗体源码, 需要直接拉取代码的可以去gitee
- CRC冗余检测带窗体版
需要API版本可以点击一下连接直接拉取
- CRC冗余检测带API接口版本,springboot实现
接口访问可以使用
http://localhost:8080/getResult/{{被除数}}/{{除数}}
访问可以得出冗余码
http://localhost:8080/checking/{{被除数}}/{{除数}}
通过手动修改被除数之后,就是在原除数最后添加上冗余码 再进行访问。
结果如图所示
这里的出的冗余码是:011
将冗余添加到最后在进行检测,得出结果是000,这就证明了冗余检测通过, 其中有具体每一步的计算结果(在传输的序列中),可以自行解析json整合到自己的项目中