C++与JAVA代码实现CRC-16/MODBUS算法,且与 http://www.ip33.com/crc.html 进行结果验证
CRC-16/MODBUS的多项式为:x16+x15+x2+1(8005),宽度为16。运算时,首先将一个16位的寄存器预置为11111111 11111111,然后连续把数据帧中的每个字节中的8位与该寄存器的当前值进行运算。仅仅每个字节的8位数据位参与生成CRC。
在生成CRC时,每个字节的8位与寄存器中的内容进行异或,然后将结果向低位位移,高位则用0补充,最低位(LSB)移出并检测,如果是1,该寄存器就与一个预设的固定值(0A001H)进行一次异或运算,如果低位为0,不作任何处理。
上述处理重复进行,直到执行完了8次位移操作,当最后一位移完以后,下一个8位字节与寄存器当前的值进行异或运算,同样进行上述的另一轮8次位移异或才做,当数据帧中的所有字节都做了处理,生成最终值就是CRC值。
生成CRC的流程为:
1. 预置一个16位寄存器位FFFFH,称之为CRC寄存器。
2. 把数据帧中第一个字节的8位与CRC寄存器中的低字节进行异或运算,结果存回CRC寄存器。
3. 将CRC寄存器向右移1位,最高位以0填充,最低位移出并监测。
4. 如果最低位为0:重复第3步(下一次移位),如果最低位为1:将CRC寄存器与一个预设的固定值(0A001H)进行异或运算。
5. 重复第3步和第4步直到8次位移,这样就处理完了一个完整的8位。
6. 重复第2步到第5步来处理下一个字节,知道处理完校验位前所有的数据。
7. 最终CRC寄存器得值就是CRC的值。
C++代码实现
#include <stdio.h>
#include<iostream>
#include<string>
#include<sstream>
using namespace std; unsigned short getCrc16(unsigned char *arr, int len); string intToHexStr(int data); int main()
{
unsigned char arr[] = {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00};
int len = ;
int crcInt;
crcInt = getCrc16(arr, len);
printf( "crc16 int is: %d\n", crcInt );
string crcStr;
crcStr = intToHexStr(crcInt);
cout << "crc16 hex is: " << crcStr.c_str() <<endl;
return ;
} /**
* 进行CRC-6/MODBUS计算
*/
unsigned short getCrc16(unsigned char *arr, int len)
{
unsigned short ax,lsb;
int i,j;
ax = 0xFFFF;
for (i = ; i < len; i++)
{
ax ^= arr[i];
for (j = ; j < ; j++)
{
lsb = ax & 0x0001;
ax = ax >> ;
if (lsb != )
{
ax ^= 0xA001;
}
}
}
return ax;
} /**
* 将int类型数据转换成16进制字符串
*/
string intToHexStr(int data)
{
/*** 将int类型数据转换成16进制字符串 ***/
string hexStr;
ostringstream temp;
temp<<hex<<data;
hexStr = temp.str(); /*** 将小写转大写 ***/
int i;
int len = hexStr.size();
for (i = ; i < len; i++) {
hexStr[i] = toupper(hexStr[i]);
} /*** 保证将字符串长度为4 ***/
while (hexStr.size() < ) {
hexStr = "" + hexStr;
} /*** 返回 ***/
return hexStr;
}
JAVA代码实现
package com.yanwu.demo.utils; import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection; /**
* @author <a herf="mailto:yanwu0527@163.com">yanwu</a>
* @date 2019-08-26 14:22.
* <p>
* description:
* 本方法使用CRC-16/MODBUS算法
*/
@SuppressWarnings("all")
public class Crc16Util {
private static final Integer ONE = 1;
private static final Integer TWO = 2;
private static final Integer HEX = 16;
private static final String NUL = "";
private static final String SPACE = " ";
private static final String ASCII = "US-ASCII"; /**
* 根据报文byte数组,获取CRC-16 16进制字符串<p>
* 48 4C 01 00 01 00 00 05 00 00 >> 0xE647
*
* @param data 报文数组
* @return CRC值(16进制)
*/
public static String getCrc16HexStr(String data) {
return crcToHexStr(getCrc16(data));
} /**
* 根据报文byte数组,获取CRC-16 int值<p>
* 48 4C 01 00 01 00 00 05 00 00 >> 58951
*
* @param data 报文数组
* @return CRC值(10进制)
*/
public static int getCrc16(String data) {
if (StringUtils.isBlank(data)) {
// ----- 校验:报文字符串不能为空,否则抛异常
throw new RuntimeException("The string cannot be empty!");
}
return getCrc16(hexStrToByteArr(data));
} /**
* 根据报文byte数组,获取CRC-16 16进制字符串<p>
* {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 0xE647
*
* @param data 报文数组
* @return CRC值(16进制)
*/
public static String getCrc16HexStr(byte[] data) {
return crcToHexStr(getCrc16(data));
} /**
* 根据报文byte数组,获取CRC-16 int值<p>
* {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00} >> 58951
*
* @param data 报文数组
* @return CRC值(10进制)
*/
public static int getCrc16(byte[] data) {
if (data.length == 0) {
// ----- 校验:报文数组不能为空,否则抛异常
throw new RuntimeException("The array cannot be empty!");
}
// ----- 预置一个CRC寄存器,初始值为0xFFFF
int crc = 0xFFFF;
byte byteLen;
boolean flag;
for (byte item : data) {
// ----- 循环,将每数据帧中的每个字节与CRC寄存器中的低字节进行异或运算
crc ^= ((int) item & 0x00FF);
byteLen = 8;
while (byteLen > 0) {
// ----- 判断寄存器最后一位是 1\0:[true: 1; false: 0]
flag = (crc & ONE) == ONE;
// ----- 将寄存器右移1位,最高位自动补0
crc >>= 1;
if (flag) {
// ----- 如果右移出来的位是 1:将寄存器与固定值 0xA001 异或运算
// ----- 如果右移出来的位是 0:不做处理,进行下一次右移
// ----- 直到处理完整个字节的8位
crc ^= 0xA001;
}
byteLen--;
}
}
// ----- 最终寄存器得值就是CRC的值,返回
return crc;
} /**
* 将16进制字符串转换为16进制Byte数组<p>
* 48 4C 01 00 01 00 00 05 00 00 >> {0x48, 0x4C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00}
*
* @param str 报文字符串
* @return 报文数组
*/
private static byte[] hexStrToByteArr(String str) {
str = str.replaceAll(SPACE, NUL);
int strLen = str.length();
if ((strLen & ONE) == ONE) {
// ----- 报文字符串必须是以一个字节为单位(两位为一个字节),所以当去除所有空格后的报文长度为单数时说明报文错误
throw new RuntimeException("Incorrect message format!");
}
byte[] result = new byte[strLen / TWO];
// ----- 两位一个字节
for (int i = 0; i < strLen; i += TWO) {
String temp = str.substring(i, i + TWO);
result[i / TWO] = (byte) Integer.parseInt(temp, HEX);
}
return result;
} /**
* 将CRC-16值转换成16进制字符串,且保持最小长度为4位<p>
* 58951 >> E647
*
* @param data CRC值(10进制)
* @return CRC值(16进制)
*/
private static String crcToHexStr(int data) {
String crcStr = Integer.toHexString(data).toUpperCase();
int size = 4 - crcStr.length();
StringBuilder builder = new StringBuilder();
// ---- 长度不够 4 位高位自动补0
while (size > 0) {
builder.append("0");
size--;
}
return builder.append(crcStr).toString();
} /**
* 输出16进制与长度, 提供给 C++ CRC校验方法 测试 代码使用
*
* @param str 16进制字符串
*/
private static void printHexStr(String str) {
String[] split = str.split(SPACE);
StringBuilder builder = new StringBuilder();
builder.append(" unsigned char arr[] = {");
for (int i = 0; i < split.length; i++) {
builder.append("0x").append(split[i]);
if (i < split.length - 1) {
builder.append(", ");
}
}
builder.append("};");
System.out.println(builder.toString());
System.out.println(" int len = " + split.length + ";");
} /**
* 测试CRC获取
*
* @param args
*/
public static void main(String[] args) throws Exception {
String str = "48 4C 01 00 01 00 00 05 00 00";
// ----- 输出16进制数组给 C++ 测试使用
Crc16Util.printHexStr(str);
// ----- 获取CRC-16的值
System.out.println("crc16 int is: " + Crc16Util.getCrc16(str));
System.out.println("crc16 hex is: " + Crc16Util.getCrc16HexStr(str));
// ----- 与 http://www.ip33.com/crc.html 进行结果验证
check();
} private static void check() {
char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
// ----- 校验的次数
int size = Integer.MAX_VALUE;
int len = 0, min = 10, max = 500;
StringBuilder thisData = new StringBuilder();
StringBuilder httpData = new StringBuilder();
while (size > 0) {
thisData.delete(0, thisData.length());
httpData.delete(0, httpData.length());
len = RandomUtils.nextInt(min, max);
while (len > 0) {
char h = chars[RandomUtils.nextInt(0, 16)];
char l = chars[RandomUtils.nextInt(0, 16)];
httpData.append(h).append(l);
thisData.append(h).append(l);
if (len != 1) {
httpData.append("+");
thisData.append(SPACE);
}
len--;
}
String thisCrc = getCrc16HexStr(thisData.toString());
String httpCrc = getCrcByUrl(httpData.toString());
System.out.println("this: " + thisCrc + " -> " + thisData.toString());
System.out.println("http: " + httpCrc + " -> " + httpData.toString());
if (!thisCrc.equals(httpCrc)) {
throw new RuntimeException("ERROR!!!");
}
size--;
}
} public static String getCrcByUrl(String data) {
BufferedReader in = null;
String result = "";
try {
String path = "http://api.ip33.com/crc/c?width=16&poly=8005&init=FFFF&xor=0000&refin=true&refout=true&data=" + data;
URL realUrl = new URL(path);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
System.out.println("responce -> " + result);
int index = result.indexOf("\"hex\": \"") + 8;
return result.substring(index, index + 4);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return null;
} }
C++与JAVA代码实现CRC-16/MODBUS算法,且与 http://www.ip33.com/crc.html 进行结果验证的更多相关文章
- 【Java】 大话数据结构(16) 排序算法(3) (堆排序)
本文根据<大话数据结构>一书,实现了Java版的堆排序. 更多:数据结构与算法合集 基本概念 堆排序种的堆指的是数据结构中的堆,而不是内存模型中的堆. 堆:可以看成一棵完全二叉树,每个结点 ...
- java代码之美(16) ---Java8 Optional
Java8 Optional 一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题. 一.前言 在我们开放过程中,碰到的异常中 ...
- 常用的Java代码汇总
1. 字符串有整型的相互转换 Java 1 2 <strong>Stringa=String.valueOf(2); //integer to numeric ...
- 20个常用java代码段
下面是20个非常有用的Java程序片段,希望能对你有用. 1. 字符串有整型的相互转换 String a = String.valueOf(2); //integer to numeric strin ...
- 怎样编写高质量的java代码
代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...
- Lombok 安装、入门 - 消除冗长的 java 代码
lombok 提供了简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 java 代码. lombok 的官方网址:http://projectlombok.org/ lombok 安装1. ...
- eclipse 4.5.2 源码修改 格式化Java代码
注:本文代码基于eclipse4.5.2 1. 需求:在换电脑之后,如何不用配置eclipse就可以很快进入开发呢,并保持原来的编码规范. 2. 方法:修改eclipse源码 分别修改了两个jar包2 ...
- Java代码获取NTP服务器时间
apache的commons-net包下面有ntp相关的实现类,主要类有: 1 org.apache.commons.net.ntp.NTPUDPClient ? 1 org.apache.com ...
- spring jdbc分离数据库代码和java代码
读取配置文件类 package com.eshore.ismp.contract.sql; import java.io.FileInputStream; import java.io.FileNot ...
随机推荐
- Veristand学习札记(3)- CD的开发
转载:https://blog.csdn.net/mfcjishiben/article/details/79417739 1 CustomDevice开发 CD的开发必须遵照NI提供的模板进行.安装 ...
- Django项目:CRM(客户关系管理系统)--21--13PerfectCRM实现King_admin分页页数
{#table_data_list.html#} {## ————————08PerfectCRM实现King_admin显示注册表的字段表头————————#} {% extends 'king_m ...
- 常用web字体的使用指南
而真正的挑战在于中文字体,由于中文字体组成的特殊性导致其体积过于庞大,除了操作系统内置的字体之外,我们很难在网站上应用其他的字体.在可选性很差的前提之下,如何正确的使用中文字体呢? 首先,以下的字体声 ...
- 基于RBAC权限管理的后台管理系统
在摸爬滚打中渐渐理解了RBAC权限管理是个什么玩意. RBAC的基本概念: **RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构成了访问权限三元 ...
- Hackerrank--Stock Maximize(DP Practice)
题目链接 Your algorithms have become so good at predicting the market that you now know what the share p ...
- 开户项目的sql查询语句备查
查询全国所有的券商名录 SELECT id,security,province,city,borough FROM t_security GROUP BY security ; 查询某个省份的所有券商 ...
- 2019-3-1-安装-Sureface-Hub-系统-Windows-10-team-PPIPro-系统
title author date CreateTime categories 安装 Sureface Hub 系统 Windows 10 team PPIPro 系统 lindexi 2019-03 ...
- tcpdump概述
tcpdump是一个用于截取网络分组,并输出分组内容的工具.tcpdump凭借强大的功能和灵活的截取策略,使其成为类UNIX系统下用于网络分析和问题排查的首选工具. tcpdump提供了源代码,公开了 ...
- Leetcode501.Find Mode in Binary Search Tree二叉搜索树中的众数
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素). 假定 BST 有如下定义: 结点左子树中所含结点的值小于等于当前结点的值 结点右子树中所含结点的值大于等于当 ...
- 模拟15 题解(waiting)
T1 60%算法 定义f[i][j]表示枚举到i位置,已经使用过了j个队, $f[i][j]+=f[i-1][t] ( t \in [max(0,j-k),j])$滚动一下 这是个O(n^3)的,考虑 ...