一、概况

  作为Java世界使用最广泛的网络通信框架Netty,其性能和效率是有目共睹的,好多大公司都在使用如苹果、谷歌、Facebook、Twitter、阿里巴巴等,所以不仅仅是因为Netty有高效的性能与效率,更重要的是:屏蔽了底层的复杂度,简单易懂的编程模型,适应更广泛的应用场景,以及活跃的开发者社区。
  本篇博客是作为Netty之数据编码的续篇,上一篇以抛砖引玉的方式讲解了怎么使用Netty的核心缓冲区ByteBuf怎么编码存储各种基本数据,本篇就是与之对应的怎么从缓冲区ByteBuf中的编码数据解码出来,因为我们的Java代码中处理数据一般不是按照字节流来处理,所以需要解码恢复出数据然后再进行处理。

二、代码实现

1. 解码工具类

  1 package com.ethan.cws.common.utils;
2
3 import com.ethan.cws.common.enums.TypeEnum;
4 import io.netty.buffer.ByteBuf;
5 import io.netty.buffer.ByteBufUtil;
6 import io.netty.util.CharsetUtil;
7
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.List;
11
12 /**
13 * 解码工具类
14 *
15 * @author ethancws
16 * @date
17 */
18 public final class DecodeUtils {
19
20 /**
21 * FEP data数据文件后缀名
22 */
23 public final static String FILE_SUFFIX_EXTEND = ".xml";
24
25 /**
26 * 文件名
27 */
28 public final static String FILE_NAME = "Filename";
29
30 private DecodeUtils() {
31
32 }
33
34 /**
35 * 解码
36 *
37 * @param symbol 符号
38 * @param byteNum 字节数
39 * @param buff 数据
40 * @param type 枚举类型字符串
41 * @param endian 编码
42 * @return 解码数据
43 */
44 public static Object decode(String symbol, int byteNum, ByteBuf buff,
45 String type, boolean endian) {
46 Object value = null;
47 //类型枚举
48 final TypeEnum typeEnum = TypeEnum.match(type);
49 switch (typeEnum) {
50 case TYPE_STRING:
51 case TYPE_ENUM_STRING:
52 case TYPE_DATE_STRING:
53 value = readString(byteNum, buff, symbol);
54 break;
55 case TYPE_HEX_STRING:
56 case TYPE_ENUM_HEX_STRING:
57 value = readHexString(byteNum, buff);
58 break;
59 case TYPE_USHORT:
60 value = readUnSignShort(buff, endian);
61 break;
62 case TYPE_SHORT:
63 value = readShort(buff, endian);
64 break;
65 case TYPE_INT:
66 case TYPE_ENUM_INT:
67 value = readInt(buff, endian);
68 break;
69 case TYPE_UINT:
70 value = readUnSignInt(buff, endian);
71 break;
72 case TYPE_BYTE:
73 case TYPE_ENUM_BYTE:
74 value = readByte(buff);
75 break;
76 case TYPE_UBYTE:
77 value = readUnSignByte(buff);
78 break;
79 case TYPE_BIT:
80 value = readBit(byteNum, buff);
81 break;
82 case TYPE_MULTI_BIT:
83 value = readMultiBit(byteNum, buff);
84 break;
85 case TYPE_BCD8421:
86 value = readBcd8421(byteNum, buff);
87 break;
88
89 }
90
91 return value;
92 }
93
94 /**
95 * 读无符号byte
96 *
97 * @param buff 编码数据
98 * @return 解码数据
99 */
100 public static short readUnSignByte(ByteBuf buff) {
101 byte by = buff.readByte();
102 return (short) (by & 0x0FF);
103 }
104
105 /**
106 * 读byte
107 *
108 * @param buff 编码数据
109 * @return 解码数据
110 */
111 public static byte readByte(ByteBuf buff) {
112 return buff.readByte();
113 }
114
115 /**
116 * 读无符号int
117 *
118 * @param buff 编码数据
119 * @param endian 字节序
120 * @return 解码数据
121 */
122 public static long readUnSignInt(ByteBuf buff, boolean endian) {
123 int intValue = endian ? buff.readIntLE() : buff.readInt();
124 return intValue & 0x0FFFFFFFFL;
125 }
126
127 /**
128 * 读int
129 *
130 * @param buff 编码数据
131 * @param endian 字节序
132 * @return 解码数据
133 */
134 public static int readInt(ByteBuf buff, boolean endian) {
135 return endian ? buff.readIntLE() : buff.readInt();
136 }
137
138 /**
139 * 读short
140 *
141 * @param buff 编码数据
142 * @param endian 字节序
143 * @return 解码数据
144 */
145 public static short readShort(ByteBuf buff, boolean endian) {
146 return endian ? buff.readShortLE() : buff.readShort();
147 }
148
149 /**
150 * 读无符号short
151 *
152 * @param buff 编码数据
153 * @param endian 字节序
154 * @return 解码数据
155 */
156 public static int readUnSignShort(ByteBuf buff, boolean endian) {
157 short shortValue = endian ? buff.readShortLE() : buff.readShort();
158 return shortValue & 0x0FFFF;
159 }
160
161 /**
162 * 读Hex字符串
163 *
164 * @param num 字节长度
165 * @param buff 编码数据
166 * @return 字符串
167 */
168 public static String readHexString(int num, ByteBuf buff) {
169 String value = ByteBufUtil.hexDump(buff, 0, num);
170 readByteBuf(num, buff);
171 return value;
172 }
173
174 /**
175 * 读Hex字符串没有数据缓冲区偏移
176 *
177 * @param num 字节长度
178 * @param buff 编码数据
179 * @return 字符串
180 */
181 public static String readHexStringWithoutOffset(int num, ByteBuf buff) {
182 return ByteBufUtil.hexDump(buff, 0, num);
183 }
184
185 /**
186 * 获取文件名称
187 *
188 * @param fileName 字符
189 * @return 文件名称
190 */
191 private static String acquireFileName(String fileName) {
192 String fileSuffixExtend = FILE_SUFFIX_EXTEND;
193 int index = fileName.lastIndexOf(fileSuffixExtend);
194 index += fileSuffixExtend.length();
195 fileName = fileName.substring(1, index);
196 return fileName;
197 }
198
199 /**
200 * 读字符串
201 *
202 * @param num 字节长度
203 * @param buff 编码数据
204 * @param symbol 编码标识
205 * @return 字符串
206 */
207 public static String readString(int num, ByteBuf buff, String symbol) {
208 final CharSequence charSequence = buff.getCharSequence(0, num, CharsetUtil.UTF_8);
209 String value = charSequence.toString();
210 if (FILE_NAME.equals(symbol)) {
211 value = acquireFileName(value);
212 }
213 //移动读指针
214 readByteBuf(num, buff);
215 return value;
216 }
217
218
219 /**
220 * 移动读指针
221 *
222 * @param num 移动字节数
223 * @param buff 数据缓冲区ByteBuf
224 */
225 private static void readByteBuf(int num, ByteBuf buff) {
226 assert num >= 1;
227 if (num == 1) {
228 buff.readByte();
229 } else {
230 buff.readBytes(num);
231 }
232 }
233
234 /**
235 * 读bit
236 *
237 * @param num 字节长度
238 * @param buff 数据缓冲区ByteBuf
239 * @return bit位索引
240 */
241 public static int readBit(int num, ByteBuf buff) {
242 ByteBuf buffCopy = buff.copy(0, num);
243 int index = 0;
244 for (; num > 0; num--) {
245 byte b = buffCopy.readByte();
246 if (b != 0) {
247 index += b / 2;
248 --num;
249 break;
250 }
251 }
252 index += num * 8;
253 //移动读指针
254 readByteBuf(num, buff);
255 return index;
256 }
257
258 /**
259 * 读多位bit
260 *
261 * @param num 字节长度
262 * @param buff 数据缓冲区ByteBuf
263 * @return 二进制数据为1的索引数组
264 */
265 public static int[] readMultiBit(int num, ByteBuf buff) {
266 ByteBuf buffCopy = buff.copy(0, num);
267 List<Integer> list = new ArrayList<>();
268 int size = num;
269 final int fixedNum = num;
270 for (; num > 0; num--) {
271 size--;
272 int b = readUnSignByte(buffCopy);
273 if (b != 0) {
274 String str = Integer.toBinaryString(b);
275 str = fullFillByteString(str);
276 gatherIndexes(str, size, list);
277 }
278 }
279 //移动读指针
280 readByteBuf(fixedNum, buff);
281 return Arrays.stream(list.toArray(new Integer[0])).mapToInt(Integer::valueOf).toArray();
282 }
283
284 /**
285 * 补全byte二进制8位字符串
286 *
287 * @param str 字符串
288 * @return 补全8位后的字符串
289 */
290 private static String fullFillByteString(String str) {
291 int len = 8;
292 int length = str.length();
293 if (length < 8) {
294 StringBuilder strBuilder = new StringBuilder(str);
295 for (int i = 0; i < len - length; i++) {
296 strBuilder.insert(0, "0");
297 }
298 str = strBuilder.toString();
299 }
300 return str;
301 }
302
303 /**
304 * 收集索引存入List
305 *
306 * @param str byte二进制字符串
307 * @param size 剩余byte长度
308 * @param list 集合List
309 */
310 private static void gatherIndexes(String str, int size, List<Integer> list) {
311 int len = 8, lenFixed = 8;
312 for (char ch : str.toCharArray()) {
313 int totalIndex = 0;
314 len--;
315 if (ch == 48) {
316 continue;
317 }
318 totalIndex = len + size * lenFixed;
319 list.add(totalIndex);
320 }
321 }
322
323 /**
324 * 读Bcd码
325 *
326 * @param num 字节长度
327 * @param buff 数据缓冲区ByteBuf
328 * @return Bcd码解码数据
329 */
330 public static String readBcd8421(int num, ByteBuf buff) {
331 return readHexString(num, buff);
332 }
333 }

2. 数据类型枚举类

  1 package com.ethan.cws.common.enums;
2
3 /**
4 * 数据枚举
5 *
6 * @author ethancws
7 * @date
8 */
9 public enum TypeEnum {
10 /**
11 * 字符串
12 */
13 TYPE_STRING("string"),
14
15 /**
16 * Binary-Coded Decimal
17 * bcd码 8421码
18 * 4位二进制数表示1位十进制数
19 */
20 TYPE_BCD8421("bcd8421"),
21 /**
22 * 时间字符串
23 */
24 TYPE_DATE_STRING("date_string"),
25 /**
26 * 枚举byte
27 */
28 TYPE_ENUM_BYTE("enum|byte"),
29
30 /**
31 * 枚举int
32 */
33 TYPE_ENUM_INT("enum|int"),
34
35 /**
36 * 枚举字符串
37 */
38 TYPE_ENUM_STRING("enum|string"),
39
40 /**
41 * 枚举HEX字符串
42 */
43 TYPE_ENUM_HEX_STRING("enum|hex_string"),
44
45 /**
46 * HEX字符串
47 */
48 TYPE_HEX_STRING("hex_string"),
49
50 /**
51 * -2^31~2^31-1
52 * -2,147,483,648~2,147,483,647
53 */
54 TYPE_INT("int"),
55 /**
56 * 0~2^32
57 * 0~4294967296L
58 */
59 TYPE_UINT("uint"),
60 /**
61 * -2^15~2^15-1
62 * -32768~32767
63 */
64 TYPE_SHORT("short"),
65 /**
66 * 0~65535
67 */
68 TYPE_USHORT("ushort"),
69 /**
70 * -2^7~2^7-1
71 * -128~127
72 */
73 TYPE_BYTE("byte"),
74
75 /**
76 * 0~256
77 */
78 TYPE_UBYTE("ubyte"),
79
80 /**
81 * 多位同选
82 */
83 TYPE_MULTI_BIT("multi_bit"),
84 /**
85 * 位
86 */
87 TYPE_BIT("bit");
88
89 private String val;
90
91 TypeEnum(String val) {
92 this.val = val;
93 }
94
95
96 /**
97 * 字符串匹配枚举类型
98 *
99 * @param value 字符串
100 * @return 对应枚举
101 */
102 public static TypeEnum match(String value) {
103 String str = "TYPE_";
104 if (value.indexOf("|") > 0) {
105 value = value.replace("|", "_");
106 }
107 str += value.toUpperCase();
108 return valueOf(str);
109 }
110
111
112 }

三、后记

  随着对于Netty的理解和使用的深入,越来越对于Netty框架的痴迷,所以后面会不定期的更新Netty相关的使用与心得。欢迎与大家一起探讨一起学习。

Netty之数据解码的更多相关文章

  1. netty之编解码

    1.netty的编码和解码,在数据传输的时候,考虑数据安全,数据完整性都是很有必要的.这里主要是介绍netty3和netty5的编解码方式.其实从StringEncoder和StringDecoder ...

  2. 在dubbo的一端,看Netty处理数据包,揭网络传输原理

    如今,我们想要开发一个网络应用,那是相当地方便.不过就是引入一个框架,然后设置些参数,然后写写业务代码就搞定了. 写业务代码自然很重要,但是你知道: 你的数据是怎么来的吗?通过网络传输过来的呗. 你知 ...

  3. 一文搞懂 Netty 发送数据全流程 | 你想知道的细节全在这里

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 在<Netty如何高效接收网络数据 ...

  4. Netty入门系列(3) --使用Netty进行编解码的操作

    前言 何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输.B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常 ...

  5. java架构之路-(netty专题)netty的编解码(出入战)与粘包拆包

    上次回归: 上次博客我们主要说了netty的基本使用,都是一些固定的模式去写的,我们只需要关注我们的拦截器怎么去写就可以了,然后我们用我们的基础示例,改造了一个简单的聊天室程序,可以看到内部加了一个S ...

  6. WebSocket帧数据 解码/转码

    数据从浏览器通过websocket发送给服务器的数据,是原始的帧数据,默认是被掩码处理过的,所以需要对其利用掩码进行解码. 从服务器发送给浏览器的数据是默认没有掩码处理的,只要符合一定结构就可以了.具 ...

  7. Android -- 获取摄像头帧数据解码

    由于Android下摄像头预览数据只能  ImageFormat.NV21 格式的,所以解码时要经过一翻周折. Camera mCamera = Camera.open(); Camera.Param ...

  8. Android 关于获取摄像头帧数据解码

    由于Android下摄像头预览数据只能  ImageFormat.NV21 格式的,所以解码时要经过一翻周折. Camera mCamera = Camera.open(); Camera.Param ...

  9. Netty返回数据丢包的问题之一

    这个问题是在一个群友做压力测试的时候发现的.使用客户端和netty创建一条连接,然后写了一个for循环不停的给服务器发送1500条信息,发现返回只有几百条.另外几百条不知道哪去了.查看代码,发现在服务 ...

  10. \x 开头编码的数据解码成中文

    在python里,直接decode('utf-8')即可 >>> "\xE5\x85\x84\xE5\xBC\x9F\xE9\x9A\xBE\xE5\xBD\x93 \xE ...

随机推荐

  1. python学习记录(六)-系统内置模块

    序列化 什么是序列化?序列化是指把python中的数据以文本或二进制形式进行转换,还能反序列化为原来的数据 为什么需要序列化?便于数据在程序与网络之间的传输和存储 json:文本序列化 pickle: ...

  2. k8s基本操作

    注意:k8s的很多操作需要指定命名空间,如果不指定,默认default的命名空间,很多东西就查不出来了 kubectl get cs # 查看集群状态kubectl get nodes # 查看集群节 ...

  3. SqlServer获取一周内每天的金额统计数据

    select datename(weekday,CAST([CompletedTime] AS date)) WeekNum, CAST([CompletedTime] AS date) AS 'Da ...

  4. Sleuth 使用

    sleuth是spring cloud中日志链(调用链解决方案),自动添加traceId,spanId. sleuth包也可单独引入,和其它藕合度小.sleuth + zipkin 实现APM管理. ...

  5. LaravelORM 中的 withSum , withAvg, withMax,withMin 的实现

    Orm::withCount(['relation as relation_sum' =>function($query){ $query->select(DB::raw("su ...

  6. MySQL基础随笔记

    [1]SQL语言入门      我们都知道,数据库管理人员(DBA)通过数据库管理系统(DBMS)可以对数据库(DB)中的数据进行操作,但具体是如何操作的呢?这就涉及到我们本节要讲的SQL语言.SQL ...

  7. Android笔记--FileProvider

    FileProvider介绍 继承于ContentProvider,本质上依旧是用于跨境通信,对第三方应用暴露文件,并授予文件读写地权限 具体内容 1.在Strings.xml里面配置一个常量 2.在 ...

  8. Python学习笔记--序列+集合+字典

    序列 切片:从一个序列中,取出一个子序列 注意: 案例: 实现: 集合 无序性.唯一性 添加新元素: .add 移除元素: .remove 随机取出某个元素: 清空集合: .clear 取两个集合的差 ...

  9. Spring--数据库资源管理遗留问题

    遗留问题的解决 在我们要再试一试其他属性的时候,就出现了一些小问题:定义的情况下, 在.xml文件里面调用: 却发现输出是这样的: 这完全不对等啊! 之后发现是系统的值,优先级要高于我们自己配置的这个 ...

  10. 被冰封的 Bug:Fishhook Crash 修复纪实

    作者:郝连福,业界资深计算机技术专家,现任声网Agora 首席前端架构师.先后担任过 Principal Engineer/Engineering Director(UTStarcom).Sr. ar ...