Netty之数据解码

2023-06-20,,

一、概况

  作为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之数据解码的相关教程结束。

《Netty之数据解码.doc》

下载本文的Word格式文档,以方便收藏与打印。