[toc]
## Byte數組操作
Bit 意為"位"或"比特",是計算機運算的基礎單位;Byte 意為"字節",是計算機文件大小的基本計算單位。1 bit 就是1位二進制數,比如 1 或者 0;1 Byte 就是 1 個字節,1 個字節是由8個二進制位組成的。比如1111111,00000000等。
Byte是java中的八大基本數據類型之一,byte是Byte的拆箱數據類型。
### 數據轉換
我們在開發過程中,編輯器使用的數值默認是10進制的,畢竟十進制是普及到街巷市井的一個數制,即便是我們懂得二進制的人,十進制的可讀性也比較習慣。而對計算機則不然,因為它的底層存儲機制為二進制,但是二進制的讀取對我們人類而言則不是那么方便,8位二進制正好可以使用2個十六進制數字表示,相比二進制,表述也更加簡便。
舉個例子,比如二進制表示表示一個字節的值為10110110,使用十六進制則可以簡單表示為B6。
### 數組截取
本案例中,我們經常會遇到字節數組的截取。
`System.arraycopy(src, srcPos, dest, destPos, length)`的使用:
```java
/* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
* @exception IndexOutOfBoundsException if copying would cause
* access of data outside array bounds.
* @exception ArrayStoreException if an element in the <code>src</code>
* array could not be stored into the <code>dest</code> array
* because of a type mismatch.
* @exception NullPointerException if either <code>src</code> or
* <code>dest</code> is <code>null</code>.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
```
參數解釋:
- Object src : 原數組
- int srcPos : 從原數組中截取數據的起始位置
- Object dest : 目標數組
- int destPos : 目標數組的開始起始位置
- int length : 從原數組中截取數據時要copy的數組的長度
比如 :我們有一個數組數據
```java
byte[] srcBytes = new byte[]{2,4,0,0,0,0,0,10,15,50}; // 源數組
byte[] destBytes = new byte[5]; // 目標數組
System.arrayCopy(srcBytes,0,destBytes ,0,5);
```
則最終destBytes中的元素為:`{2,4,0,0,0}`。
### 數組合并
關于數組合并,我們還會經常用到,`commons-lang3.jar`包中的`ArrayUtils`類的`addAll()`,其功能是添加一個字節數組或者添加一個字節到原先的字節數組中。
```java
public static byte[] addAll(byte[] array1, byte... array2) {
if (array1 == null) {
return clone(array2);
} else if (array2 == null) {
return clone(array1);
} else {
byte[] joinedArray = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, joinedArray, 0, array1.length);
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
return joinedArray;
}
}
```
### ByteUtil工具類
這里提供一個用于16進制字符串與數值轉換的工具類,尤其是在我們單元測試中會經常用來調試。
```java
import org.apache.commons.lang3.ArrayUtils;
import java.io.*;
public class ByteUtil {
/** * 16進制字符集 */
private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
private static final String HEXS = "0123456789abcdef";
/** * 將一個字節數組轉換成一個int */
public static int toInt(byte[] bytes) {
int i = 24;
int result = 0;
for (byte tmp : bytes) {
result += (tmp & 0xff) << i;
i -= 8;
}
return result;
}
/**
* 高位字節在前
* @param n
* @return
*/
public static byte[] intToHighByteArr(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xff);
b[2] = (byte) (n >> 8 & 0xff);
b[1] = (byte) (n >> 16 & 0xff);
b[0] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* 高位字節在前
* @param n
* @return
*/
public static byte[] shortToHighByteArr(short n) {
byte[] b = new byte[2];
b[1] = (byte) (n & 0xff);
b[0] = (byte) (n >> 8 & 0xff);
return b;
}
/**
* 從一個byte[]數組中截取一部分
* @param src
* @param begin
* @param count
* @return
*/
public static byte[] subBytes(byte[] src, int begin, int count) {
byte[] bs = new byte[count];
for (int i=begin;i<begin+count; i++) {
bs[i-begin] = src[i];
}
return bs;
}
/**
* 更新src數組
* @param des
* @param desPos
* @param count
* @return
*/
public static byte[] modifyBytes(byte[] src,byte[] des, int desPos, int count) {
System.arraycopy(src,0,des,desPos,count);
return des;
}
/** * 將一個字節數組轉換成一個short */
public static short toShort(byte[] bytes) {
int i = 8;
short result = 0;
for (byte tmp : bytes) {
result += (tmp & 0xff) << i;
i -= 8;
}
return result;
}
public static String toVersion(byte[] bytes){
StringBuffer stringBuffer = new StringBuffer("V");
for (int i =0 ; i< bytes.length; i++){
if (i>0){
stringBuffer.append(".");
}
stringBuffer.append(bytes[i]);
}
return stringBuffer.toString();
}
/**
* @功能: BCD碼轉為10進制串(阿拉伯數據)
* @參數: BCD碼
* @結果: 10進制串
*/
public static String bcd2Str(byte[] bytes) {
StringBuffer temp = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
temp.append((byte) ((bytes[i] & 0xf0) >>> 4));
temp.append((byte) (bytes[i] & 0x0f));
}
return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp
.toString().substring(1) : temp.toString();
}
/**
* @功能: BCD碼轉為10進制串(阿拉伯數據)
* @參數: BCD碼
* @結果: 10進制串
*/
public static String bcd62Str(byte[] bytes) {
byte[] prefix = {0x20};
byte[] all = ArrayUtils.addAll(prefix, bytes);
return ByteUtil.bcd2Str(all);
}
/**
* * 將字節數組中指定區間的子數組轉換成16進制字符串 * @param bytes 目標字節數組 * @param start
* 起始位置(包括該位置) * @param end 結束位置(不包括該位置) * @return 轉換結果
*/
public static String bytesToHex(byte bytes[], int start, int end) {
StringBuilder sb = new StringBuilder();
for (int i = start; i < start + end; i++) {
sb.append(byteToHex(bytes[i]));
}
return sb.toString();
}
/**
* hexStr:010400xxxx
* @param bytes
* @return
*/
public static String bytesToHex(byte bytes[]) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(byteToHex(bytes[i]));
}
return sb.toString();
}
/**
* 用作hex字符串的生成
* byte[]轉hex字符串
* @param array
* @return
*/
public static String bytes2HexString(byte[] array) {
StringBuilder builder = new StringBuilder();
for (byte b : array) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
if (hex.startsWith("8") || hex.startsWith("9") || hex.startsWith("a") || hex.startsWith("b") || hex.startsWith("c") || hex.startsWith("d") || hex.startsWith("e") || hex.startsWith("f")){
builder.append("(byte)");
}
builder.append("0x").append(hex).append(",");
}
return builder.toString();
}
//byte[]轉hex字符串
public static String idcardHex(byte[] array) {
StringBuilder builder = new StringBuilder();
for (byte b : array) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() > 1) {
throw new NumberFormatException("格式不正確");
}
builder.append(hex);
}
return builder.toString();
}
/** * 將單個字節碼轉換成16進制字符串 * @param bt 目標字節 * @return 轉換結果 */
public static String byteToHex(byte bt) {
return HEX_DIGITS[(bt & 0xf0) >> 4] + "" + HEX_DIGITS[bt & 0xf];
}
/**
* 將16進制轉換成字節數組
*
* @param hex
* @return
*/
public static byte[] hexToBytes(String hex) {
int len = (hex.length() / 2);
byte[] result = new byte[len];
char[] achar = hex.toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (hexToByte(achar[pos]) << 4 | hexToByte(achar[pos + 1]));
}
return result;
}
/**
* 將一個16進制轉換成字節數組
*
* @param c
* @return
*/
public static byte hexToByte(char c) {
return (byte) HEXS.indexOf(c);
}
/**
* 從對象獲取一個字節數組
*
*/
public static byte[] obj2Byte(Serializable obj) {
if (obj == null) {
return null;
}
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = null;
byte[] result = null;
try {
oo = new ObjectOutputStream(bo);
oo.writeObject(obj);
result = bo.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (oo != null) {
try {
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* 從字節數組獲取對象
*
*/
public static Object byte2Obj(byte[] objBytes) {
if (objBytes == null || objBytes.length == 0) {
return null;
}
ByteArrayInputStream bi = new ByteArrayInputStream(objBytes);
ObjectInputStream oi = null;
Object obj = null;
try {
oi = new ObjectInputStream(bi);
obj = oi.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (oi != null) {
try {
oi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return obj;
}
}
```
比如下面的例子:我們可以直接使用該工具類把16進制編碼的消息串,轉換為字節數組。
```
/**
* 返回應答消息
* @param requestBytes
* @return
*/
@Test
public void messageDeal(){
String businessMsg = "5b0000006c01fa3a8a210001406f4501020f000000000021030000004cd5e3474436353736320000000200010c675e01044b620220210615011000010cae28044b8bf7202106150117003030393838313700000000000000000000000000000005001800000001001000d07c5d";
byte[] bytes = ByteUtil.hexToBytes(businessMsg);
try {
buildResponseWithAdmin(bytes,"127.0.0.1");
} catch (CredentialNotFoundException e) {
e.printStackTrace();
}
}
```
- 第一章 開篇寄語
- 1-1 技術選型要點
- 1-2 認識905.4王國的交流規范
- 1-3 聯系作者
- 第二章 Socket編程的基礎知識
- 2-1 Socket家族的基石
- 2-2 byte數組基礎
- 2-3 緩沖區基礎
- 2-4 NIO Socket通訊的工作原理
- 第三章 905.4規范解讀
- 3-1 基于通道選擇器的Socket長連接及消息讀寫框架
- 3-2 嚴格的信件收發員
- 3-3 負責消息處理的一家子
- 3-4 負責認證的大兒子(AuthWorker)
- 3-5 啞巴老二(PingWoker)
- 3-6 勤奮的定位匯報員老三(LocationReportWorker)
- 3-7 精明的老四(BusinessReportWorker)
- 3-8 數據檢察官——CRC16-CCITT校驗
- 3-11 數據的加密官
- 3-12 頭尾標識轉義
- 第四章 測試方法
- 4-1 測試數據樣例
- 4-2 客戶端鏈路保持功能實現
- 4-3 使用Socket短連接進行功能測試
- 4-4 NIO服務端性能分析
- 4-5 http測試方法(推薦)
- 第五章 從NIO到netty
- 5-1 編程進階——Netty核心基礎
- 5-2 Netty使用常見問題
- 5-3 使用Netty重寫Server端
- 5-4 Netty之鏈路管理
- 5-5 netty堆外內存泄漏如何應對?
- 第六章 統計與監控
- 6-1 Grafana監控面板
- 第七章 售后服務
- 7-1 勘誤與優化
- 7-2 獲取源碼