今天收到的一份需求任務是對接硬件,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