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 例子

schema校验详细教程 Java java实现crc校验码生成_开发语言

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++;
    }
}

**

代码运行结果

太长的二进制计算会导致计算过程显示不全

schema校验详细教程 Java java实现crc校验码生成_java_02

如果输入的字符较短时可以得出具体的过程

schema校验详细教程 Java java实现crc校验码生成_schema校验详细教程 Java_03

注意

上面的源码是完整的窗体源码, 需要直接拉取代码的可以去gitee

  • CRC冗余检测带窗体版

需要API版本可以点击一下连接直接拉取

  • CRC冗余检测带API接口版本,springboot实现

接口访问可以使用

http://localhost:8080/getResult/{{被除数}}/{{除数}}

访问可以得出冗余码

http://localhost:8080/checking/{{被除数}}/{{除数}}

通过手动修改被除数之后,就是在原除数最后添加上冗余码 再进行访问。

结果如图所示

schema校验详细教程 Java java实现crc校验码生成_开发语言_04

这里的出的冗余码是:011

schema校验详细教程 Java java实现crc校验码生成_schema校验详细教程 Java_05

将冗余添加到最后在进行检测,得出结果是000,这就证明了冗余检测通过, 其中有具体每一步的计算结果(在传输的序列中),可以自行解析json整合到自己的项目中