工做中常常遇到須要Android程序與各式各樣的板子、智能設備進行交互,通訊的方式也基本上都是Ble
或者Socket tcp/udp
等等.....其中最重要的一點就是通訊的協議
、協議
、協議
重要的是說三遍;通訊協議就是用來定義與設備交互的方式和交互過程當中數據包的格式 如:(包頭—數據包長度—數據—校驗位—包尾)
java
一、咱們寫的代碼最終都是轉化成各類機器所能識別的二進制或者字節碼,不一樣的編程語言具備不一樣的數據類型
基本的也好不基本的也好、固然有不一樣的也就有相同的byte
(字節)就是其中的一個; 二、平常開發中咱們進行通訊發送的內容最終都會以字節的形式進行發送,這裏以Java
的Socket
爲例,咱們來看下源碼編程
Socket socket = new Socket(ip, port);
OutputStream outputStream = socket.getOutputStream();
//發送數據
outputStream.write("Hello World!".getBytes());
outputStream.flush();
//關閉鏈接
outputStream.close();
socket.close();
複製代碼
OutputStream
的wirte(byte[] b)
函數//1 接着又調用了write(byte b[], int off, int len)
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
//2 最後又調用了write(byte b)
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
//3 這裏就是講咱們發送的一個bye[]進行for循環一個個寫入了
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
//4 end
public abstract void write(int b) throws IOException;
複製代碼
byte[](字節數組)
進行發送,因此只要將數據轉成字節數組便可,下面進入數據類型
科普時間數據類型 | 所佔字節數 | 所佔bit數 | 取值範圍 |
---|---|---|---|
byte | 1 | 8 | -128 ~ 127 |
char | 2 | 16 | '\u0000' ~ '\uFFFF' |
short | 2 | 16 | -2^15 ~ 2^15 - 1 |
int | 4 | 32 | -2^31 ~ 2^31 - 1 |
float | 4 | 32 | 2^-149 ~ 2^128 -1 |
long | 8 | 64 | -2^63 ~ 2^63 - 1 |
double | 8 | 64 | 2^-1074 ~ 2^1024 - 1 |
boolean | / | 1 | true or false |
位
也就是二進制
數據,取值只有 0
,1
- 高位在左,低位在右
0111 1011
/** * byte轉8 bit * * @param b byte * @return 高位到低位順序, 以byte123 爲例: 0111 1011 */
public static byte[] byte2Bit(byte b) {
byte[] arr = new byte[8];
for (int i = 7; i >= 0; i--) {
arr[i] = (byte) (b & 1);
b = (byte) (b >> 1);
}
return arr;
}
複製代碼
/** * 8個bit位轉爲byte */
public static byte bit2Byte(byte[] bytes) {
if (bytes.length != 8) return 0;
String binary = "";
byte result;
for (byte b : bytes) {
binary += b;
}
if (bytes[0] == 0) {
// 正數
result = (byte) Integer.parseInt(binary, 2);
} else {
// 負數
result = (byte) (Integer.parseInt(binary, 2) - 256);
}
return result;
}
複製代碼
int
了int
佔4
個字節32
個bit
String s = Integer.toBinaryString(35235);
//輸出結果
1000100110100011
複製代碼
0
因此就直接省略了,固然咱們也能夠主動補齊32
位只須要在高位補0
便可。int result = Integer.parseInt("1000100110100011", 2);
//輸出結果
35235
複製代碼
String radix = Integer.toBinaryString(-35235);
System.out.println(radix);
int result = Integer.parseInt(radix, 2);
System.out.println(result);
複製代碼
程序執行會報一個java.lang.NumberFormatException: For input string:"11111111111111110111011001011101"
異常,那咱們怎麼將負數的轉回爲int呢?固然是有方法的啦,以下:數組
//須要藉助 BigInteger類
String radix = Integer.toBinaryString(-3535);
BigInteger integer = new BigInteger(radix, 2);
System.out.println(integer.intValue());
//輸出結果
-3535
複製代碼
int
佔32
個字節也就是4
個byte
,那理所固然一個int
能夠轉成2個byte或者4
個byte
,以下:/** * 一個int轉2個字節的byte數組 * 由低位到高位的轉換 * * @param value * @return */
public static byte[] intTo2Bytes(int value) {
byte[] src = new byte[2];
src[0] = (byte) (value & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
return src;
}
/** * 一個int轉4個字節的byte數組 * 由低位到高位的轉換 * * @param value * @return */
public static byte[] intTo4Bytes(int value) {
byte[] src = new byte[4];
src[0] = (byte) (value & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[3] = (byte) ((value >> 24) & 0xFF);
return src;
}
複製代碼
這裏須要注意的是
int轉byte[]
的時候是高位
在數組的0下標
仍是低位
在數組的0下標
,上面的兩個方法都是低位在數組的0下標
socket
這裏須要解釋下
uchar
、uint
是什麼意思?uchar = unsigned char 、uint = unsigned int,也就是無符號的數據,也就是表示了這個數據是正數
tcp
包頭
+擴展數據包
,其中包頭佔固定的32個字節字節
包頭
內一共包含6個字段,分別表示以下:
"DH"
,總共佔2
個字節1.0
,總共佔2
個字節"extlen"
,總共佔4
個字節取值0或1
,總共佔1
個字節0
補齊,總共佔3
個字節0
補齊,總共佔20
個字節//magic
byte[] magicB = {'D', 'H'};
//協議版本1.0轉成int也就是1
byte[] versionB = {0, 1};
//擴展數據長度,這裏假定擴展數據的長度爲67
byte[] extLenB = intTo4Bytes(67);
//擴展數據類型 0:JSON、1:二進制數據;這裏使用JSON
byte[] extType = {0};
//兩個保留字段,直接0補齊,上面已經分析了兩個字段一共佔23個字節
byte[] reserved = new byte[23];
//這裏將上面的多個數據合併至一個byte[]
byte[] data = byteMergerAll(magicB, versionB, extLenB, extType, reserved);
複製代碼
到這裏包頭的數據就已經處理好了,還能夠進一步對它進行封裝編程語言
/** * 多個數組合並一個 * * @return */
public static byte[] byteMergerAll(byte[]... bytes) {
int allLength = 0;
for (byte[] b : bytes) {
allLength += b.length;
}
byte[] allByte = new byte[allLength];
int countLength = 0;
for (byte[] b : bytes) {
System.arraycopy(b, 0, allByte, countLength, b.length);
countLength += b.length;
}
return allByte;
}
複製代碼
/** * 封裝包頭數據 * 固定32個字節,其他的0補齊 * * @param extLen 擴展數據長度 */
public static byte[] getPkgHead(int extLen) {
//magic
byte[] magicB = {'D', 'H'};
//協議版本1.0轉成int也就是1
byte[] versionB = {0, 1};
//擴展數據長度,這裏假定擴展數據的長度爲67
byte[] extLenB = intTo4Bytes(extLen);
//擴展數據類型 0:JSON、1:二進制數據;這裏使用JSON
byte[] extType = {0};
//兩個保留字段,直接0補齊,上面已經分析了兩個字段一共佔23個字節
byte[] reserved = new byte[23];
//這裏將上面的多個數據合併至一個byte[]
return byteMergerAll(magicB, versionB, extLenB, extType, reserved);
}
複製代碼
//擴展數據:這裏就須要根據實際的文檔來生成了,我這裏就隨便寫一個了
String extData = "{\"id\":12,\"cmd\":\"open\"}";
byte[] extDataB = extData.getBytes();
//獲取包頭
byte[] pkgHead = getPkgHead(extDataB.length);
//一個完整的數據包
byte[] sendData = byteMergerAll(pkgHead, extDataB);
複製代碼