TCP通信实现对接硬件发送与接收十六进制数据 & int与byte的转换原理 & java中正负数的表示
今天收到的一份需求任务是对接硬件,TCP通信,并给出通信端口与数据包格式,如下:
1.首先编写了一个简单的十六进制转byte[]数组与byte[]转换16进制字符串的两个方法,如下:
/** * 将十六进制的字符串转换成字节数组 * * @param hexString * @return */ public static byte[] hexStrToByteArrs(String hexString) { if (StringUtils.isEmpty(hexString)) { return null; } hexString = hexString.replaceAll(" ", ""); int len = hexString.length(); int index = 0; byte[] bytes = new byte[len / 2]; while (index < len) { String sub = hexString.substring(index, index + 2); bytes[index / 2] = (byte) Integer.parseInt(sub, 16); index += 2; } return bytes; } /** * 数组转换成十六进制字符串 * * @param byte[] * @return HexString */ public static final String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); } return sb.toString(); }
测试:
String string = "C0 10 00 00 00 02 04 00 01 00 00 a2 6f"; byte[] bs = { -64, 16, 0, 0, 0, 2, 4, 0, 1, 0, 0, -94, 111 }; System.out.println(Arrays.toString(hexStrToByteArrs(string))); System.out.println(bytesToHexString(bs));
结果:
[-64, 16, 0, 0, 0, 2, 4, 0, 1, 0, 0, -94, 111]
C010000000020400010000A26F
补充:这里说明一下简单的十六进制转byte与byte转十六进制的方法:
以 十六进制的 C0,也就是十进制的192为例子。
(1)十六进制转byte:
// 1.先转为In类型 int parseInt = Integer.parseInt("c0", 16); // 2.强转为byte byte b = (byte) parseInt; System.out.println(parseInt); System.out.println(b);
结果:
192
-64
在这里也明白了实际我们调用Integer.parseInt(str)的时候默认传的是十进制,如下:
public static int parseInt(String s) throws NumberFormatException { return parseInt(s,10); }
- 注意:
int占4个字节,byte占1个字节,1个字节占8位,那么强制类型转换int型截取低8位,对数据也不会造成影响。
如果再从byte型转换成int型呢。int强制转换为byte型数据时,会产生一个-128~127的有符号字节,所以byte转int的时候需要根据符号判断。
如下:
int intNum = 192; byte byteNum = (byte) intNum; int intNum2 = byteNum; System.out.println(intNum); System.out.println(byteNum); System.out.println(intNum2);
结果:
192
-64
-64
正确的byte转int是需要考虑byte的符号的,如下:
int intNum = 192; byte byteNum = (byte) intNum; int intNum2 = byteNum > 0 ? byteNum : byteNum + 256; System.out.println(intNum); System.out.println(byteNum); System.out.println(intNum2);
结果:
192
-64
192
- 计算机表示正负数(想着明白一下转换原理)
关于计算机表示正负数的方法:
1. 负数在计算机中的表示为 取反+1,取反+1成为这个数的二进制补码。
2.最高位为符号位,1负,0正。
以上面的int类型192为例子,其二进制表示为:(前面的xxx表示24个0,也就是前面3个byte都是0)
000...(24个0) 11000000
其转换为byte之后是舍掉前3byte,取低八位,就只剩下 11000000。
11000000: 由于第一位是符号位,1代表负数,所以其计算方法是取反加1 (取反之后是: 00111111,加1之后是01000000),转换为十进制就是 -64 。
再以十进制的128为例子:
其int型位数如下: 000...(24个0) 10000000
转换为byte之后为 10000000
由于1表示为负数,所以先取反为01111111,再加上00000001之后就是10000000,计算结果就是-128。
int intNum = 128; byte byteNum = (byte) intNum; int intNum2 = byteNum > 0 ? byteNum : byteNum + 256; System.out.println(intNum); System.out.println(byteNum); System.out.println(intNum2);
结果:
128
-128
128
(2)byte转16进制的字符串
byte b = -64; int intNum2 = b > 0 ? b : b + 256; String string = Integer.toString(intNum2, 16); System.out.println(string);
结果:
c0
这里需要明白:byte转为int需要根据符号进行转换,原因参考上面的补充;然后调用Integer.toString(num,radix)即可实现int转换十六进制字符串。
2.Java实现TCP协议发送十六进制数据(将十六进制数据转换为byte[])和接收byte数据并转成16进制字符串
服务端:(也就是模拟硬件,接受byte[]数据并转成16进制)
package zd.dms.socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Arrays; public class Server { public static void main(String[] args) throws IOException { // 1:建立服务器端的tcp socket服务,必须监听一个端口 ServerSocket ss = new ServerSocket(24992); // 2: 通过服务器端的socket对象的accept方法获取连接上的客户端对象 Socket s = null; // 3:获取客户端的数据 while (true) { // 接受Socket服务,如果有,没有则堵塞,等待 s = ss.accept(); System.out.println("accept success......."); try { // 从Socekt输入流中获取客户端发送过来的输出流 InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println("从客户端传送来的数据如下:"); System.out.println(Arrays.toString(buf)); // 通过服务器端Socket输出流,写数据,会传送到客户端Socket输入流中 OutputStream out = s.getOutputStream(); String retunStr = "C0 01 01 03 FF 00 C0"; out.write(SocketUtils.hexStrToByteArrs(retunStr)); } catch (Exception e) { System.out.println("error"); } finally { s.close(); } } } }
客户端:模拟发送十六进制数据并且接收十六进制数据
package zd.dms.socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import zd.dms.service.config.SystemConfigManager; public class SocketUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SocketUtils.class); private static Socket socket = null; private static String archivesCenterAPIIP = StringUtils .defaultString(SystemConfigManager.getInstance().getECMProps("archivesCenterAPIIP"), "127.0.0.1"); private static String archivesCenterAPIPort = StringUtils .defaultString(SystemConfigManager.getInstance().getECMProps("archivesCenterAPIPort"), "24992"); public static boolean connection() { if (socket != null) { return true; } try { socket = new Socket(archivesCenterAPIIP, NumberUtils.toInt(archivesCenterAPIPort)); return true; } catch (Exception e) { LOGGER.error("connection error", e); return false; } } public static void stop() { try { if (socket != null) { socket.close(); socket = null; } } catch (Exception e) { LOGGER.error("connection error", e); } } /** * 发送数据 * * @param cmd * 需要发送的数据(十六进制的字符串形式) * @return 接受到的数据(十六进制的字符串形式) */ public static String sendCmd(String cmd) { if (!connection() || socket == null) { return "error"; } try { OutputStream out = socket.getOutputStream(); byte[] hexStrToByteArrs = hexStrToByteArrs(cmd); if (hexStrToByteArrs == null) { return "error"; } out.write(hexStrToByteArrs); InputStream in = socket.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); stop(); return bytesToHexString(buf); } catch (IOException e) { LOGGER.error("sendCmd error", e); return "error"; } } /** * 将十六进制的字符串转换成字节数组 * * @param hexString * @return */ public static byte[] hexStrToByteArrs(String hexString) { if (StringUtils.isEmpty(hexString)) { return null; } hexString = hexString.replaceAll(" ", ""); int len = hexString.length(); int index = 0; byte[] bytes = new byte[len / 2]; while (index < len) { String sub = hexString.substring(index, index + 2); bytes[index / 2] = (byte) Integer.parseInt(sub, 16); index += 2; } return bytes; } /** * 数组转换成十六进制字符串 * * @param byte[] * @return HexString */ public static final String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); // 在这里故意追加一个逗号便于最后的区分 sb.append(" "); } return sb.toString(); } public static void main(String[] args) { System.out.println(sendCmd("0f 0f")); System.out.println(sendCmd("0f 0f")); } }
先启动服务端,然后启动服务端之后查看控制台:
服务器控制台:
客户端控制台:
总结:
目前来看是可行的,但是还没有去对接硬件,在对接完成之后再继续补充此方法是否可以成功的实现对接硬件并向硬件发送命令。
验证完之后也是可行的。
补充:十进制数字转换二进制、八进制和16进制字符串的方法:
System.out.println(Integer.toBinaryString(25));// 转换为二进制字符串 System.out.println(Integer.toOctalString(25));// 转换为8进制字符串 System.out.println(Integer.toHexString(25));// 转换为16进制字符串
11001
31
19
补充:字符串按照进制转换为十进制数的方法:
System.out.println(Integer.parseInt("11001", 2));// 二进制字符串转换十进制数 System.out.println(Integer.parseInt("31", 8));// 8进制字符串转换十进制数 System.out.println(Integer.parseInt("19", 16));// 16进制字符串转换十进制数
25
25
25