工做中常常遇到须要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);
复制代码