Netty之網絡編程數據編碼
一、概況
我們在進行網絡編程中會把各種數據轉換為byte數據以便能在網絡上傳輸,最常見的網絡位元組序——Little-Endian和Big-Endian,也讓好多初進網絡編程的新手摸不着頭腦,還有按位或多位存儲數據,按位或多位數據,BCD編碼,ASCII編碼,有符號數與無符號數的編碼與解碼等等。本篇博文所貼代碼並不是最簡潔最優化的,只是起到拋磚引玉的效果,不用像博主一樣剛入門網絡編程時經歷那麼的曲折,不過現在回想起來也是滿滿的收穫。
二、代碼實現
1. 編碼工具類
1 package com.chansen.common.utils; 2 3 4 import com.chansen.common.enums.TypeEnum; 5 import io.netty.buffer.ByteBuf; 6 import io.netty.buffer.ByteBufAllocator; 7 import io.netty.buffer.ByteBufUtil; 8 9 import java.util.Arrays; 10 import java.util.Date; 11 import java.util.List; 12 import java.util.Map; 13 import java.util.stream.Collectors; 14 import java.util.stream.IntStream; 15 16 /** 17 * 編碼工具類 18 * 19 * @author CHANSEN 20 * @date 21 */ 22 public final class EncodeUtils { 23 24 25 private EncodeUtils() { 26 } 27 28 29 30 31 32 33 34 /** 35 * 組裝配置文件所配置的ByteBuf 36 * 37 * @param type 枚舉類型字符串 38 * @param param 參數 39 * @param buf ByteBuf緩存域 40 * @param endian 位元組序 41 */ 42 public static void encode(String type, Object param, 43 ByteBuf buf, boolean endian) { 44 45 //類型枚舉 46 final TypeEnum typeEnum = TypeEnum.match(type); 47 //根據不同的類型編碼數據 48 switch (typeEnum) { 49 //有符號int 50 case TYPE_INT: 51 writeInt(param, buf, endian); 52 break; 53 //無符號int 54 case TYPE_UINT: 55 writeUnSignInt(param, buf, endian); 56 break; 57 //有符號short 58 case TYPE_SHORT: 59 writeShort(param, buf, endian); 60 break; 61 //無符號short 62 case TYPE_USHORT: 63 writeUnSignShort(param, buf, endian); 64 break; 65 //有符號byte 66 case TYPE_BYTE: 67 writeByte(param, buf); 68 break; 69 //無符號byte 70 case TYPE_UBYTE: 71 writeUnSignByte(param, buf); 72 break; 73 //字符串 74 case TYPE_STRING: 75 writeString(param, buf); 76 break; 77 //字符串時間 78 case TYPE_DATE_STRING: 79 writeDateString(param, buf); 80 break; 81 case TYPE_HEX_STRING: 82 writeHexString(param, buf); 83 break; 84 case TYPE_BIT: 85 writeBit(param, buf); 86 break; 87 case TYPE_MULTI_BIT: 88 writeMultiBit(param, buf); 89 break; 90 case TYPE_BCD8421: 91 writeBcd8421(param, buf); 92 break; 93 } 94 } 95 96 /** 97 * 組裝ByteBuff 98 * 99 * @param type 枚舉類型字符串 100 * @param param 參數 101 * @param rangeList 枚舉範圍 102 * @param buf ByteBuf緩存域 103 * @param endian 位元組序 104 */ 105 public static void encodeEnum(String type, Object param, 106 List<Object> rangeList, 107 ByteBuf buf, boolean endian) { 108 //枚舉數據類型 109 final TypeEnum typeEnum = TypeEnum.match(type); 110 111 switch (typeEnum) { 112 case TYPE_ENUM_BYTE: 113 writeEnumByte(param, rangeList, buf); 114 break; 115 case TYPE_ENUM_INT: 116 writeEnumInt(param, rangeList, buf, endian); 117 break; 118 case TYPE_ENUM_STRING: 119 writeEnumString(param, rangeList, buf); 120 break; 121 case TYPE_ENUM_HEX_STRING: 122 writeEnumHexString(param, rangeList, buf); 123 break; 124 } 125 126 } 127 128 /** 129 * 寫枚舉Hex字符串 130 * 131 * @param obj 數據 132 * @param list 枚舉範圍 133 * @param buff ByteBuf緩存區 134 */ 135 public static void writeEnumHexString(Object obj, List<Object> list, ByteBuf buff) { 136 for (Object object : list) { 137 if (object.toString().equals(obj.toString())) { 138 writeHexString(obj, buff); 139 } 140 } 141 142 } 143 144 /** 145 * 寫枚舉字符串 146 * 147 * @param obj 數據 148 * @param list 枚舉範圍 149 * @param buff ByteBuf緩存區 150 */ 151 public static void writeEnumString(Object obj, List<Object> list, ByteBuf buff) { 152 for (Object object : list) { 153 if (object.toString().equals(obj.toString())) { 154 writeString(obj, buff); 155 } 156 } 157 158 } 159 160 /** 161 * 寫枚舉int 162 * 163 * @param obj 數據 164 * @param list 枚舉範圍 165 * @param buff ByteBuf緩存區 166 */ 167 public static void writeEnumInt(Object obj, List<Object> list, ByteBuf buff, boolean endian) { 168 for (Object object : list) { 169 if (object.toString().equals(obj.toString())) { 170 writeInt(obj, buff, endian); 171 } 172 } 173 174 } 175 176 /** 177 * 寫枚舉byte 178 * 179 * @param obj 數據 180 * @param list 枚舉範圍 181 * @param buff ByteBuf緩存區 182 */ 183 public static void writeEnumByte(Object obj, List<Object> list, ByteBuf buff) { 184 for (Object object : list) { 185 if (object.toString().equals(obj.toString())) { 186 writeByte(obj, buff); 187 } 188 } 189 190 } 191 192 /** 193 * 寫字符串數據 194 * 195 * @param obj 值 196 * @param buf ByteBuf緩存區 197 */ 198 public static void writeHexString(Object obj, ByteBuf buf) { 199 String value = (String) obj; 200 writeHexString(value, buf); 201 } 202 203 /** 204 * 寫字符串數據 205 * 206 * @param value 值 207 * @param buf ByteBuf緩存區 208 */ 209 public static void writeHexString(String value, ByteBuf buf) { 210 //value is hexDump 211 final byte[] bytes = ByteBufUtil.decodeHexDump(value); 212 buf.writeBytes(bytes); 213 } 214 215 /** 216 * 寫時間字符串數據 217 * 218 * @param obj 值 219 * @param buf ByteBuf緩存區 220 */ 221 public static void writeDateString(Object obj, ByteBuf buf) { 222 Date value = (Date) obj; 223 writeDateString(value, buf); 224 } 225 226 /** 227 * 寫時間字符串數據 228 * 229 * @param obj 值 230 * @param buf ByteBuf緩存區 231 */ 232 public static void writeDateString(Date obj, ByteBuf buf) { 233 String value = DateTimeUtils.getFormatDateTime(obj); 234 writeString(value, buf); 235 } 236 237 /** 238 * 寫字符串數據 239 * 240 * @param obj 值 241 * @param buf ByteBuf緩存區 242 */ 243 public static void writeString(Object obj, ByteBuf buf) { 244 String value = (String) obj; 245 writeString(value, buf); 246 } 247 248 249 /** 250 * 寫字符串數據 251 * 252 * @param value 值 253 * @param buf ByteBuf緩存區 254 */ 255 public static void writeString(String value, ByteBuf buf) { 256 final char[] valueChars = value.toCharArray(); 257 if (valueChars.length > 0) { 258 for (char valueChar : valueChars) { 259 buf.writeByte(valueChar); 260 } 261 } 262 } 263 264 /** 265 * 寫int數據 266 * 267 * @param obj 值 268 * @param buf ByteBuf緩存區 269 * @param endian 位元組序 270 */ 271 public static void writeInt(Object obj, ByteBuf buf, boolean endian) { 272 int m = (int) obj; 273 //小位元組序 274 if (endian) { 275 buf.writeIntLE(m); 276 } else { 277 buf.writeInt(m); 278 } 279 } 280 281 /** 282 * 寫無符號byte數據 283 * 284 * @param obj 值 285 * @param buf ByteBuf緩存區 286 */ 287 public static void writeUnSignByte(Object obj, ByteBuf buf) { 288 int m = (int) obj; 289 writeUnSignByte(m, buf); 290 } 291 292 /** 293 * 寫無符號byte數據 294 * 295 * @param m 值 296 * @param buf ByteBuf緩存區 297 */ 298 public static void writeUnSignByte(int m, ByteBuf buf) { 299 writeUnSignByteBase(m, buf); 300 } 301 302 /** 303 * 寫byte數據 304 * 305 * @param obj 值 306 * @param buf ByteBuf緩存區 307 */ 308 public static void writeByte(Object obj, ByteBuf buf) { 309 int m = (int) obj; 310 assert m <= 127 && m >= -128; 311 buf.writeByte(m); 312 } 313 314 /** 315 * 寫無符號short數據 316 * 317 * @param obj 值 318 * @param buf ByteBuf緩存區 319 * @param endian 位元組序 320 */ 321 public static void writeUnSignShort(Object obj, ByteBuf buf, boolean endian) { 322 int m = (int) obj; 323 assert m >= 0 && m <= 65535; 324 m &= 0x0FFFF; 325 writeShort(m, buf, endian); 326 } 327 328 /** 329 * 寫short數據 330 * 331 * @param obj 值 332 * @param buf ByteBuf緩存區 333 * @param endian 位元組序 334 */ 335 public static void writeShort(Object obj, ByteBuf buf, boolean endian) { 336 int m = (short) obj; 337 //-32768~32767 338 assert m >= -32768 && m <= 32767; 339 writeShort(m, buf, endian); 340 } 341 342 /** 343 * 寫無符號int數據 344 * 345 * @param obj 值 346 * @param buf ByteBuf緩存區 347 * @param endian 位元組序 348 */ 349 public static void writeUnSignInt(Object obj, ByteBuf buf, boolean endian) { 350 long m = (long) obj; 351 assert m >= 0 && m < 0x100000000L; 352 String hexString = Long.toHexString(m); 353 hexString = fullFillHexString(hexString); 354 final byte[] bytes = hexEncodeBytes(hexString, 4); 355 //小位元組序 356 if (endian) { 357 final byte[] littleBytes = {bytes[3], bytes[2], bytes[1], bytes[0]}; 358 buf.writeBytes(littleBytes); 359 } else { 360 buf.writeBytes(bytes); 361 } 362 } 363 364 /** 365 * hex字符串轉byte數組 366 * 367 * @param hexString hex字符串 368 * @return byte數組 369 */ 370 public static byte[] hexEncodeBytes(String hexString, int index) { 371 final byte[] bytes = ByteBufUtil.decodeHexDump(hexString); 372 int len = bytes.length; 373 byte[] bytesTmp = new byte[index]; 374 if (len < index) { 375 byte[] bt = ByteBufUtil.decodeHexDump("00"); 376 for (int i = 0; i < (index - len); i++) { 377 bytesTmp[i] = bt[0]; 378 } 379 } 380 381 for (int j = bytes.length - 1; j >= 0; j--) { 382 bytesTmp[--index] = bytes[j]; 383 } 384 return bytesTmp; 385 } 386 387 /** 388 * hex字符串補位處理 389 * 390 * @param hexString hex字符串 391 * @return hex字符串 392 */ 393 public static String fullFillHexString(String hexString) { 394 int len = hexString.length(); 395 int mold = len % 2; 396 return mold > 0 ? "0" + hexString : hexString; 397 } 398 399 /** 400 * 寫short數據 401 * 402 * @param m 數據 403 * @param buf ByteBuf 404 * @param endian 位元組序 405 */ 406 public static void writeShort(int m, ByteBuf buf, boolean endian) { 407 //小位元組序 408 if (endian) { 409 buf.writeShortLE(m); 410 } else { 411 //大位元組序 412 buf.writeShort(m); 413 } 414 } 415 416 /** 417 * 寫多bit位 418 * 419 * @param obj 參數 420 * @param buf ByteBuf 421 */ 422 public static void writeMultiBit(Object obj, ByteBuf buf) { 423 int[] arr = (int[]) obj; 424 writeMultiBit(arr, buf); 425 } 426 427 /** 428 * 寫多bit位 429 * 430 * @param arr 參數 431 * @param buff ByteBuf 432 */ 433 public static void writeMultiBit(int[] arr, ByteBuf buff) { 434 int total = 0; 435 for (int i : arr) { 436 int j = 1 << i; 437 total += j; 438 } 439 writeBitBase(total, buff); 440 } 441 442 /** 443 * 寫bit位 444 * 445 * @param obj 參數 446 * @param buff ByteBuf 447 */ 448 public static void writeBit(Object obj, ByteBuf buff) { 449 int value = (int) obj; 450 writeBit(value, buff); 451 } 452 453 /** 454 * 位寫bit 455 * 456 * @param buff ByteBuf數據緩存區 457 * @param bitIndex 置位索引 458 */ 459 public static void writeBit(int bitIndex, ByteBuf buff) { 460 int i = 1 << bitIndex; 461 writeBitBase(i, buff); 462 } 463 464 /** 465 * 寫bit位基礎 466 * 467 * @param i 數據 468 * @param buff ByteBuf數據緩存區 469 */ 470 private static void writeBitBase(int i, ByteBuf buff) { 471 //buff容量 472 final int capacity = buff.capacity(); 473 assert capacity >= 1; 474 //255 475 if (i <= 0xff) { 476 int j = capacity; 477 --j; 478 fullFillBytes(buff, j); 479 buff.writeByte(i); 480 } else { 481 writeBitBaseMore(i, buff); 482 } 483 } 484 485 /** 486 * 寫bit位基礎較大值 487 * 488 * @param i 數據 489 * @param buff ByteBuf數據緩存區 490 */ 491 private static void writeBitBaseMore(int i, ByteBuf buff) { 492 final int capacity = buff.capacity(); 493 int j = capacity; 494 String hexString = Integer.toHexString(i); 495 hexString = fullFillHexString(hexString); 496 final byte[] bytes = ByteBufUtil.decodeHexDump(hexString); 497 assert bytes.length <= capacity; 498 j -= bytes.length; 499 fullFillBytes(buff, j); 500 buff.writeBytes(bytes); 501 } 502 503 /** 504 * 填充byte 505 * 506 * @param buff ByteBuf 507 * @param m 循環次數 508 */ 509 private static void fullFillBytes(ByteBuf buff, int m) { 510 for (; m > 0; m--) { 511 buff.writeByte(0x00); 512 } 513 } 514 515 /** 516 * 寫BCD碼 517 * <p> 518 * 4位二進制數表示1位十進制數 519 * <p> 520 * 20200905 521 * 522 * @param obj 數據 523 * @param buff ByteBuf數據緩存區 524 */ 525 public static void writeBcd8421(Object obj, ByteBuf buff) { 526 String value = (String) obj; 527 writeBcd8421(value, buff); 528 } 529 530 /** 531 * 寫BCD碼 532 * <p> 533 * 4位二進制數表示1位十進制數 534 * <p> 535 * 20200905 536 * 537 * @param bcdString 數據 538 * @param buff ByteBuf數據緩存區 539 */ 540 public static void writeBcd8421(String bcdString, ByteBuf buff) { 541 assert bcdString.length() == buff.capacity() * 2; 542 final char[] chars = bcdString.toCharArray(); 543 boolean flag = true; 544 int j = 0; 545 for (char ch : chars) { 546 int i = Integer.parseInt(ch + ""); 547 if (flag) { 548 j = i << 4; 549 flag = false; 550 } else { 551 j += i; 552 writeUnSignByteBase(j, buff); 553 j = 0; 554 flag = true; 555 } 556 } 557 } 558 559 /** 560 * 寫無符號數基礎 561 * 562 * @param m 數據 563 * @param buff ByteBuf數據緩存區 564 */ 565 private static void writeUnSignByteBase(int m, ByteBuf buff) { 566 assert m < 256 && m >= 0; 567 buff.writeByte(m); 568 } 569 570 }
2.數據類型枚舉類
1 package com.chansen.common.enums; 2 3 /** 4 * 數據枚舉 5 * 6 * @author CHANSEN 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 }
後記,如果大家覺得不錯,後期我會把數據解碼的部分代碼貼出來的。