Java用Socket解析字节流数据
2021/7/26 17:06:28
本文主要是介绍Java用Socket解析字节流数据,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Java用Socket解析字节流数据
- 背景
- 问题与解决
- 1. Socket连接与数据读取方式的选择
- 2. 内部数据协议的顶层解析过程
- 3. 字节数据解析成Java数据类型的问题
背景
因业务需求,需要完成一个TCP连接数据的解析与转发的插件。
最终,使用Socket进行TCP简单连接,逐步读取字节数据,解析成想要的数据类型,最后转发。
问题与解决
1. Socket连接与数据读取方式的选择
因为只是做一个Tcp连接的客户端,Socket建立使用最简单的方式:
Socket socket = new Socket(ip, port);
数据流读写方式,设计上要求插件一直等待字节数据,这里使用缓冲流能提高效率:
BufferedInputStream bi = new BufferedInputStream(socket.getInputStream());
2. 内部数据协议的顶层解析过程
public static Packet readPacket(BufferedInputStream in) throws IOException { // 阻塞等待指定长度的字节流, 这里首先等待一个完整包头的字节数据 while (in.available() < LEN_PKT_HEADER) ; // 1. 读取packet header byte[] head = new byte[LEN_PKT_HEADER]; in.read(head); // 2.解析packet header内容, getUShort是将字节数组转为无符号类型的short数据 int pktBodySize = getUShort(head[0], head[1]); // 3. 读取packet body if (pktBodySize <= 0) { // todo: 空数据包 return null; } // 阻塞等待指定长度的字节流 while (in.available() < pktBodySize) ; // 读取 byte[] pktBody = new byte[pktBodySize]; in.read(pktBody); return new Packet(0, 0, pktBody); }
3. 字节数据解析成Java数据类型的问题
这个插件中用到的协议字节格式是小端,而Java中的字节存储是大端。
需要注意的是,Java的基础数据类型是有符号的,而传入的字节流数据会包含无符号数据,重点在于最高位的bit处理,一般思路是用范围更大的有符号数据来存储无符号数据。
通过位运算来逐个字节取short类型中的bit数据,然后按照大小端顺序来拼成数组。
- short转成byte数组,int类型的转换类型
private static byte[] getBytes(short num, @Min(1) int len) { if (len == 1) { return new byte[]{(byte) (num & 0xFF)}; } else { return new byte[]{(byte) (num & 0xFF), (byte) ((num >> Byte.SIZE) & 0xFF)}; } }
- unsigned short 类型转成byte数组
public static int getUShort(byte... bytes) { int num = bytes[0] & 0xFF; if (bytes.length > 1) { num |= (bytes[1] & 0xFF) << Byte.SIZE; } return num; }
- 字符串类型的数据格式与转换
在这个协议中,字符串的表示字节数组是<LEN><TEXT>
,首先根据字节的前1位(两位,由协议本身约定)的字节,经过转成short类型得到TEXT部分的字节长度len,进而去转成String类型。
String stockCode = new String(bytes, idx, len, StandardCharsets.UTF_8);
- byte数组转成Long (有符号)
int LEN_LONG = Long.SIZE / Byte.SIZE; public static long getLong(byte[] bytes, int idx) { long res = bytes[idx + LEN_LONG - 1]; for (int i = idx + LEN_LONG - 2; i >= idx; i--) { res = (res << Byte.SIZE) | ((long) bytes[i] & 0xFFL); } return res; }
- byte数组转成unsigned long类型
因为Java中基础类型最长的字节就8个,因为需要借助更大的数据类型来保存8字节的无符号数据
public static BigDecimal getULong(byte[] bytes, int idx) { // 7 * 8 = 2的56次方, 将ULong型数的最高位字节,从第一个字节还原所需的移动的位数 final BigDecimal towOf56 = BigDecimal.valueOf((long) 1 << ((LEN_LONG - 1) * Byte.SIZE)); // 1. 保存最高位 byte high = bytes[idx + LEN_LONG - 1]; // 2. 将最高位置0 bytes[idx + LEN_LONG - 1] = 0; // 3. 将除最高字节以外的byte数组转为有符号long long tmp = getLong(bytes, idx); BigDecimal num = BigDecimal.valueOf(tmp); // 4.将最高位字节转成对应的大小的数 BigDecimal highNum = BigDecimal.valueOf(Byte.toUnsignedLong(high)); // 乘以2的56次方,等同与将这个数表示的最高字节向左移动7个字节 highNum = highNum.multiply(towOf56); return num.add(highNum); }
这篇关于Java用Socket解析字节流数据的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-19JAVA分布式id教程:轻松入门与实践
- 2024-11-19Java高并发教程:入门与实践指南
- 2024-11-19JAVA高并发直播教程:新手入门指南
- 2024-11-19Java高并发直播教程:入门与实践指南
- 2024-11-19Java微服务教程:初学者快速入门指南
- 2024-11-19JAVA微服务教程:新手入门的详细指南
- 2024-11-19Java微服务教程:从零开始搭建你的第一个微服务应用
- 2024-11-19Java项目开发教程:初学者必备指南
- 2024-11-19Java项目开发教程:新手快速入门指南
- 2024-11-19Java项目开发教程:零基础入门到实战