The previous article mainly talked about the serialization and parsing of protobuf byte streams. Although it is possible to transfer protobuf objects directly after serializing them into byte streams, it is impossible to actually transfer only protobuf byte streams in a project because there are several very common problems in tcp communication of sockets, that is, sticky packets and few packets.The so-called sticky package simply means that a socket will send several smaller packages together.Because tcp is connection-oriented, in order to send multiple packets to the receiver more effectively, the sender uses an optimization method (Nagle algorithm), which combines data with smaller intervals and a smaller amount of data into a large data block and then packages it.Fewer packages means that when the cache is full, the soket sends incomplete packages to the receiver (sticking and fewer packages are, as I understand it, a problem).This makes it possible for the receiver to receive multiple packets at a time. To solve this problem, you need to send out the length of the package before sending the data.Thus, the structure of the package should be message length + message content.
In this article, let's say data splicing, dry goods are coming
First Split Packet
1 /// <summary> 2 /// Build Message Packet 3 /// </summary> 4 /// <param name="protobufModel"></param> 5 /// <param name="packetCode"></param> 6 byte[] BuildPackage(IExtensible protobufModel, int packetCode) 7 { 8 byte[] b; 9 if (protobufModel != null) 10 b = ProtobufSerilizer.Serialize(protobufModel); 11 else 12 b = new byte[0]; 13 14 ByteBuffer buf = ByteBuffer.Allocate(b.Length + 4 + 4); 15 buf.WriteInt(b.Length + 4); 16 buf.WriteInt(packetCode); 17 18 if (protobufModel != null) 19 buf.WriteBytes(b); 20 return buf.GetBytes(); 21 }
The ByteBuffer tool java used in the code is available, but not in c#. The source code is extracted to https://www.oschina.net/code/snippet_42170_37516, but the author did not add a method to get all the byte codes in the tool, so he added a GetBytes() method himself
1 using System; 2 using System.Collections.Generic; 3 4 /// <summary> 5 /// Byte Buffer Processing Class, this class only processes large byte orders 6 /// Warning, this class is non-thread safe 7 /// </summary> 8 public class ByteBuffer 9 { 10 //Byte Cache 11 private byte[] buf; 12 //Read Index 13 private int readIndex = 0; 14 //Write Index 15 private int writeIndex = 0; 16 //Read Index Tags 17 private int markReadIndex = 0; 18 //Write index Tags 19 private int markWirteIndex = 0; 20 //Length of the cache byte array 21 private int capacity; 22 23 //Object pool 24 private static List<ByteBuffer> pool = new List<ByteBuffer>(); 25 private static int poolMaxCount = 200; 26 //Is this object pooled 27 private bool isPool = false; 28 29 /// <summary> 30 /// Construction method 31 /// </summary> 32 /// <param name="capacity">Initial capacity</param> 33 private ByteBuffer(int capacity) 34 { 35 buf = new byte[capacity]; 36 this.capacity = capacity; 37 } 38 39 /// <summary> 40 /// Construction method 41 /// </summary> 42 /// <param name="bytes">Initial byte array</param> 43 private ByteBuffer(byte[] bytes) 44 { 45 buf = bytes; 46 this.capacity = bytes.Length; 47 this.readIndex = 0; 48 this.writeIndex = bytes.Length + 1; 49 } 50 51 /// <summary> 52 /// Build a capacity Byte Cache of Length ByteBuffer object 53 /// </summary> 54 /// <param name="capacity">Initial capacity</param> 55 /// <returns>ByteBuffer object</returns> 56 public static ByteBuffer Allocate(int capacity) 57 { 58 return new ByteBuffer(capacity); 59 } 60 61 /// <summary> 62 /// Build a bytes Is Byte Cached ByteBuffer Object, generally not recommended 63 /// </summary> 64 /// <param name="bytes">Initial byte array</param> 65 /// <returns>ByteBuffer object</returns> 66 public static ByteBuffer Allocate(byte[] bytes) 67 { 68 return new ByteBuffer(bytes); 69 } 70 71 /// <summary> 72 /// Get a pooled ByteBuffer Object, pooled object must be called Dispose Then it will be pushed into the pool, otherwise this method is equivalent to Allocate(int capacity)Method, which is thread-safe 73 /// </summary> 74 /// <param name="capacity">ByteBuffer The initial capacity size of the object, if there is no object in the cache pool, then the capacity size of the object is this value, otherwise it is the actual capacity value of the object in the pool</param> 75 /// <returns></returns> 76 public static ByteBuffer GetFromPool(int capacity) 77 { 78 lock (pool) 79 { 80 ByteBuffer bbuf; 81 if (pool.Count == 0) 82 { 83 bbuf = Allocate(capacity); 84 bbuf.isPool = true; 85 return bbuf; 86 } 87 int lastIndex = pool.Count - 1; 88 bbuf = pool[lastIndex]; 89 pool.RemoveAt(lastIndex); 90 if (!bbuf.isPool) 91 { 92 bbuf.isPool = true; 93 } 94 return bbuf; 95 } 96 } 97 98 /// <summary> 99 /// according to length Length, determined to be greater than this leng The nearest 2nd power, such as length=7,Return value is 8 100 /// </summary> 101 /// <param name="length">Reference capacity</param> 102 /// <returns>The nearest second power greater than the reference capacity</returns> 103 private int FixLength(int length) 104 { 105 int n = 2; 106 int b = 2; 107 while (b < length) 108 { 109 b = 2 << n; 110 n++; 111 } 112 return b; 113 } 114 115 /// <summary> 116 /// Flip byte array, if local byte sequence is low byte sequence, then flip to high byte sequence 117 /// </summary> 118 /// <param name="bytes">Byte array to be converted to high byte order</param> 119 /// <returns>Byte Array of High Byte Sequence</returns> 120 private byte[] flip(byte[] bytes) 121 { 122 if (BitConverter.IsLittleEndian) 123 { 124 Array.Reverse(bytes); 125 } 126 return bytes; 127 } 128 129 /// <summary> 130 /// Determine the size of the internal byte cache array 131 /// </summary> 132 /// <param name="currLen">Current capacity</param> 133 /// <param name="futureLen">Future capacity</param> 134 /// <returns>Future capacity</returns> 135 private int FixSizeAndReset(int currLen, int futureLen) 136 { 137 if (futureLen > currLen) 138 { 139 //Determine internal byte cache size by double the power of the original size 140 int size = FixLength(currLen) * 2; 141 if (futureLen > size) 142 { 143 //Determine the internal byte cache size by double the future size 144 size = FixLength(futureLen) * 2; 145 } 146 byte[] newbuf = new byte[size]; 147 Array.Copy(buf, 0, newbuf, 0, currLen); 148 buf = newbuf; 149 capacity = newbuf.Length; 150 } 151 return futureLen; 152 } 153 154 /// <summary> 155 /// take bytes Byte array from startIndex Beginning length Bytes written to this cache 156 /// </summary> 157 /// <param name="bytes">Byte data to be written</param> 158 /// <param name="startIndex">Start of Write</param> 159 /// <param name="length">Write Length</param> 160 public void WriteBytes(byte[] bytes, int startIndex, int length) 161 { 162 int offset = length - startIndex; 163 if (offset <= 0) return; 164 int total = offset + writeIndex; 165 int len = buf.Length; 166 FixSizeAndReset(len, total); 167 for (int i = writeIndex, j = startIndex; i < total; i++, j++) 168 { 169 buf[i] = bytes[j]; 170 } 171 writeIndex = total; 172 } 173 174 /// <summary> 175 /// From 0 to length Write elements of 176 /// </summary> 177 /// <param name="bytes">Byte data to be written</param> 178 /// <param name="length">Write Length</param> 179 public void WriteBytes(byte[] bytes, int length) 180 { 181 WriteBytes(bytes, 0, length); 182 } 183 184 /// <summary> 185 /// Write all byte arrays to the cache 186 /// </summary> 187 /// <param name="bytes">Byte data to be written</param> 188 public void WriteBytes(byte[] bytes) 189 { 190 WriteBytes(bytes, bytes.Length); 191 } 192 193 /// <summary> 194 /// Will be a ByteBuffer Valid byte areas are written to this cache 195 /// </summary> 196 /// <param name="buffer">Byte cache to be written</param> 197 public void Write(ByteBuffer buffer) 198 { 199 if (buffer == null) return; 200 if (buffer.ReadableBytes() <= 0) return; 201 WriteBytes(buffer.ToArray()); 202 } 203 204 /// <summary> 205 /// Write a int16 data 206 /// </summary> 207 /// <param name="value">short data</param> 208 public void WriteShort(short value) 209 { 210 WriteBytes(flip(BitConverter.GetBytes(value))); 211 } 212 213 /// <summary> 214 /// Write a ushort data 215 /// </summary> 216 /// <param name="value">ushort data</param> 217 public void WriteUshort(ushort value) 218 { 219 WriteBytes(flip(BitConverter.GetBytes(value))); 220 } 221 222 /// <summary> 223 /// Write a int32 data 224 /// </summary> 225 /// <param name="value">int data</param> 226 public void WriteInt(int value) 227 { 228 //byte[] array = new byte[4]; 229 //for (int i = 3; i >= 0; i--) 230 //{ 231 // array[i] = (byte)(value & 0xff); 232 // value = value >> 8; 233 //} 234 //Array.Reverse(array); 235 //Write(array); 236 WriteBytes(flip(BitConverter.GetBytes(value))); 237 } 238 239 /// <summary> 240 /// Write a uint32 data 241 /// </summary> 242 /// <param name="value">uint data</param> 243 public void WriteUint(uint value) 244 { 245 WriteBytes(flip(BitConverter.GetBytes(value))); 246 } 247 248 /// <summary> 249 /// Write a int64 data 250 /// </summary> 251 /// <param name="value">long data</param> 252 public void WriteLong(long value) 253 { 254 WriteBytes(flip(BitConverter.GetBytes(value))); 255 } 256 257 /// <summary> 258 /// Write a uint64 data 259 /// </summary> 260 /// <param name="value">ulong data</param> 261 public void WriteUlong(ulong value) 262 { 263 WriteBytes(flip(BitConverter.GetBytes(value))); 264 } 265 266 /// <summary> 267 /// Write a float data 268 /// </summary> 269 /// <param name="value">float data</param> 270 public void WriteFloat(float value) 271 { 272 WriteBytes(flip(BitConverter.GetBytes(value))); 273 } 274 275 /// <summary> 276 /// Write a byte data 277 /// </summary> 278 /// <param name="value">byte data</param> 279 public void WriteByte(byte value) 280 { 281 int afterLen = writeIndex + 1; 282 int len = buf.Length; 283 FixSizeAndReset(len, afterLen); 284 buf[writeIndex] = value; 285 writeIndex = afterLen; 286 } 287 288 /// <summary> 289 /// Write a byte data 290 /// </summary> 291 /// <param name="value">byte data</param> 292 public void WriteByte(int value) 293 { 294 byte b = (byte)value; 295 WriteByte(b); 296 } 297 298 /// <summary> 299 /// Write a double Type data 300 /// </summary> 301 /// <param name="value">double data</param> 302 public void WriteDouble(double value) 303 { 304 WriteBytes(flip(BitConverter.GetBytes(value))); 305 } 306 307 /// <summary> 308 /// Write a character 309 /// </summary> 310 /// <param name="value"></param> 311 public void WriteChar(char value) 312 { 313 WriteBytes(flip(BitConverter.GetBytes(value))); 314 } 315 316 /// <summary> 317 /// Write a Boolean Data 318 /// </summary> 319 /// <param name="value"></param> 320 public void WriteBoolean(bool value) 321 { 322 WriteBytes(flip(BitConverter.GetBytes(value))); 323 } 324 325 /// <summary> 326 /// Read a byte 327 /// </summary> 328 /// <returns>Byte data</returns> 329 public byte ReadByte() 330 { 331 byte b = buf[readIndex]; 332 readIndex++; 333 return b; 334 } 335 336 /// <summary> 337 /// Read a byte and convert to int Type of data 338 /// </summary> 339 /// <returns>int data</returns> 340 public int ReadByteToInt() 341 { 342 byte b = ReadByte(); 343 return (int)b; 344 } 345 346 /// <summary> 347 /// Get from index Start at Index len Bytes of length 348 /// </summary> 349 /// <param name="index"></param> 350 /// <param name="len"></param> 351 /// <returns></returns> 352 private byte[] Get(int index, int len) 353 { 354 byte[] bytes = new byte[len]; 355 Array.Copy(buf, index, bytes, 0, len); 356 return flip(bytes); 357 } 358 359 /// <summary> 360 /// Start reading from the reading index position len Byte Array of Length 361 /// </summary> 362 /// <param name="len">Byte Length to Read</param> 363 /// <returns>Byte Array</returns> 364 private byte[] Read(int len) 365 { 366 byte[] bytes = Get(readIndex, len); 367 readIndex += len; 368 return bytes; 369 } 370 371 /// <summary> 372 /// Read one uint16 data 373 /// </summary> 374 /// <returns>ushort data</returns> 375 public ushort ReadUshort() 376 { 377 return BitConverter.ToUInt16(Read(2), 0); 378 } 379 380 /// <summary> 381 /// Read one int16 data 382 /// </summary> 383 /// <returns>short data</returns> 384 public short ReadShort() 385 { 386 return BitConverter.ToInt16(Read(2), 0); 387 } 388 389 /// <summary> 390 /// Read one uint32 data 391 /// </summary> 392 /// <returns>uint data</returns> 393 public uint ReadUint() 394 { 395 return BitConverter.ToUInt32(Read(4), 0); 396 } 397 398 /// <summary> 399 /// Read one int32 data 400 /// </summary> 401 /// <returns>int data</returns> 402 public int ReadInt() 403 { 404 return BitConverter.ToInt32(Read(4), 0); 405 } 406 407 /// <summary> 408 /// Read one uint64 data 409 /// </summary> 410 /// <returns>ulong data</returns> 411 public ulong ReadUlong() 412 { 413 return BitConverter.ToUInt64(Read(8), 0); 414 } 415 416 /// <summary> 417 /// Read one long data 418 /// </summary> 419 /// <returns>long data</returns> 420 public long ReadLong() 421 { 422 return BitConverter.ToInt64(Read(8), 0); 423 } 424 425 /// <summary> 426 /// Read one float data 427 /// </summary> 428 /// <returns>float data</returns> 429 public float ReadFloat() 430 { 431 return BitConverter.ToSingle(Read(4), 0); 432 } 433 434 /// <summary> 435 /// Read one double data 436 /// </summary> 437 /// <returns>double data</returns> 438 public double ReadDouble() 439 { 440 return BitConverter.ToDouble(Read(8), 0); 441 } 442 443 /// <summary> 444 /// Read a character 445 /// </summary> 446 /// <returns></returns> 447 public char ReadChar() 448 { 449 return BitConverter.ToChar(Read(2), 0); 450 } 451 452 /// <summary> 453 /// Reading Boolean Data 454 /// </summary> 455 /// <returns></returns> 456 public bool ReadBoolean() 457 { 458 return BitConverter.ToBoolean(Read(1), 0); 459 } 460 461 /// <summary> 462 /// Start reading from the reading index position len Bytes of length to disbytes In the target byte array 463 /// </summary> 464 /// <param name="disbytes">Read bytes will be stored in this byte array</param> 465 /// <param name="disstart">Write index of target byte array</param> 466 /// <param name="len">Length of Read</param> 467 public void ReadBytes(byte[] disbytes, int disstart, int len) 468 { 469 int size = disstart + len; 470 for (int i = disstart; i < size; i++) 471 { 472 disbytes[i] = this.ReadByte(); 473 } 474 } 475 476 /// <summary> 477 /// Get a byte 478 /// </summary> 479 /// <param name="index"></param> 480 /// <returns></returns> 481 public byte GetByte(int index) 482 { 483 return buf[index]; 484 } 485 486 /// <summary> 487 /// Get all bytes 488 /// </summary> 489 /// <returns></returns> 490 public byte[] GetBytes() 491 { 492 return buf; 493 } 494 495 /// <summary> 496 /// Get a double-precision floating-point data without changing its content 497 /// </summary> 498 /// <param name="index">Byte Index</param> 499 /// <returns></returns> 500 public double GetDouble(int index) 501 { 502 return BitConverter.ToDouble(Get(0, 8), 0); 503 } 504 505 /// <summary> 506 /// Get a floating point data without changing the data content 507 /// </summary> 508 /// <param name="index">Byte Index</param> 509 /// <returns></returns> 510 public float GetFloat(int index) 511 { 512 return BitConverter.ToSingle(Get(0, 4), 0); 513 } 514 515 /// <summary> 516 /// Get a long integer without changing the data content 517 /// </summary> 518 /// <param name="index">Byte Index</param> 519 /// <returns></returns> 520 public long GetLong(int index) 521 { 522 return BitConverter.ToInt64(Get(0, 8), 0); 523 } 524 525 /// <summary> 526 /// Get a reshaped data without changing its content 527 /// </summary> 528 /// <param name="index">Byte Index</param> 529 /// <returns></returns> 530 public int GetInt(int index) 531 { 532 return BitConverter.ToInt32(Get(0, 4), 0); 533 } 534 535 /// <summary> 536 /// Get a short reshaped data without changing its content 537 /// </summary> 538 /// <param name="index">Byte Index</param> 539 /// <returns></returns> 540 public int GetShort(int index) 541 { 542 return BitConverter.ToInt16(Get(0, 2), 0); 543 } 544 545 546 /// <summary> 547 /// Clear Read Bytes and Rebuild Cache 548 /// </summary> 549 public void DiscardReadBytes() 550 { 551 if (readIndex <= 0) return; 552 int len = buf.Length - readIndex; 553 byte[] newbuf = new byte[len]; 554 Array.Copy(buf, readIndex, newbuf, 0, len); 555 buf = newbuf; 556 writeIndex -= readIndex; 557 markReadIndex -= readIndex; 558 if (markReadIndex < 0) 559 { 560 markReadIndex = readIndex; 561 } 562 markWirteIndex -= readIndex; 563 if (markWirteIndex < 0 || markWirteIndex < readIndex || markWirteIndex < markReadIndex) 564 { 565 markWirteIndex = writeIndex; 566 } 567 readIndex = 0; 568 } 569 570 /// <summary> 571 /// Empty this object, but keep the byte cache array (empty array) 572 /// </summary> 573 public void Clear() 574 { 575 buf = new byte[buf.Length]; 576 readIndex = 0; 577 writeIndex = 0; 578 markReadIndex = 0; 579 markWirteIndex = 0; 580 capacity = buf.Length; 581 } 582 583 /// <summary> 584 /// Release the object, clear the byte cache array, and if the object is pooled, calling this method will push the object into the pool until the next call 585 /// </summary> 586 public void Dispose() 587 { 588 readIndex = 0; 589 writeIndex = 0; 590 markReadIndex = 0; 591 markWirteIndex = 0; 592 if (isPool) 593 { 594 lock (pool) 595 { 596 if (pool.Count < poolMaxCount) 597 { 598 pool.Add(this); 599 } 600 } 601 } 602 else 603 { 604 capacity = 0; 605 buf = null; 606 } 607 } 608 609 /// <summary> 610 /// Set up/Get Read Pointer Position 611 /// </summary> 612 public int ReaderIndex 613 { 614 get 615 { 616 return readIndex; 617 } 618 set 619 { 620 if (value < 0) return; 621 readIndex = value; 622 } 623 } 624 625 /// <summary> 626 /// Set up/Get Write Pointer Position 627 /// </summary> 628 public int WriterIndex 629 { 630 get 631 { 632 return writeIndex; 633 } 634 set 635 { 636 if (value < 0) return; 637 writeIndex = value; 638 } 639 } 640 641 /// <summary> 642 /// Index position of tag read 643 /// </summary> 644 public void MarkReaderIndex() 645 { 646 markReadIndex = readIndex; 647 } 648 649 /// <summary> 650 /// Index location for markup writing 651 /// </summary> 652 public void MarkWriterIndex() 653 { 654 markWirteIndex = writeIndex; 655 } 656 657 /// <summary> 658 /// Reset the read index position to the marked read index position 659 /// </summary> 660 public void ResetReaderIndex() 661 { 662 readIndex = markReadIndex; 663 } 664 665 /// <summary> 666 /// Reset Written Index Location to Tagged Write Index Location 667 /// </summary> 668 public void ResetWriterIndex() 669 { 670 writeIndex = markWirteIndex; 671 } 672 673 /// <summary> 674 /// Readable number of valid bytes 675 /// </summary> 676 /// <returns>Readable bytes</returns> 677 public int ReadableBytes() 678 { 679 return writeIndex - readIndex; 680 } 681 682 /// <summary> 683 /// Get a readable byte array 684 /// </summary> 685 /// <returns>Byte data</returns> 686 public byte[] ToArray() 687 { 688 byte[] bytes = new byte[writeIndex]; 689 Array.Copy(buf, 0, bytes, 0, bytes.Length); 690 return bytes; 691 } 692 693 /// <summary> 694 /// Get Cache Capacity Size 695 /// </summary> 696 /// <returns>Cache capacity</returns> 697 public int GetCapacity() 698 { 699 return this.capacity; 700 } 701 702 /// <summary> 703 /// Simple data types 704 /// </summary> 705 public enum LengthType 706 { 707 //byte type 708 BYTE, 709 //short type 710 SHORT, 711 //int type 712 INT 713 } 714 715 /// <summary> 716 /// Write a Data 717 /// </summary> 718 /// <param name="value">Data to be written</param> 719 /// <param name="type">Data type to be written</param> 720 public void WriteValue(int value, LengthType type) 721 { 722 switch (type) 723 { 724 case LengthType.BYTE: 725 this.WriteByte(value); 726 break; 727 case LengthType.SHORT: 728 this.WriteShort((short)value); 729 break; 730 default: 731 this.WriteInt(value); 732 break; 733 } 734 } 735 736 /// <summary> 737 /// Reads a value based on the type of value type Decision, int or short or byte 738 /// </summary> 739 /// <param name="type">Value type</param> 740 /// <returns>int data</returns> 741 public int ReadValue(LengthType type) 742 { 743 switch (type) 744 { 745 case LengthType.BYTE: 746 return ReadByteToInt(); 747 case LengthType.SHORT: 748 return (int)ReadShort(); 749 default: 750 return ReadInt(); 751 } 752 } 753 754 /// <summary> 755 /// Write a string 756 /// </summary> 757 /// <param name="content">String to be written</param> 758 /// <param name="lenType">Written string length type</param> 759 public void WriteUTF8String(string content, LengthType lenType) 760 { 761 byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(content); 762 int max; 763 if (lenType == LengthType.BYTE) 764 { 765 WriteByte(bytes.Length); 766 max = byte.MaxValue; 767 } 768 else if (lenType == LengthType.SHORT) 769 { 770 WriteShort((short)bytes.Length); 771 max = short.MaxValue; 772 } 773 else 774 { 775 WriteInt(bytes.Length); 776 max = int.MaxValue; 777 } 778 if (bytes.Length > max) 779 { 780 WriteBytes(bytes, 0, max); 781 } 782 else 783 { 784 WriteBytes(bytes, 0, bytes.Length); 785 } 786 } 787 788 /// <summary> 789 /// Read a string 790 /// </summary> 791 /// <param name="len">String Length to Read</param> 792 /// <returns>Character string</returns> 793 public string ReadUTF8String(int len) 794 { 795 byte[] bytes = new byte[len]; 796 this.ReadBytes(bytes, 0, len); 797 return System.Text.UTF8Encoding.UTF8.GetString(bytes); 798 } 799 800 /// <summary> 801 /// Read a string 802 /// </summary> 803 /// <param name="lenType">String Length Type</param> 804 /// <returns>Character string</returns> 805 public string ReadUTF8String(LengthType lenType) 806 { 807 int len = ReadValue(lenType); 808 return ReadUTF8String(len); 809 } 810 811 /// <summary> 812 /// Copy an object with the same data as the original without changing its data 813 /// </summary> 814 /// <returns></returns> 815 public ByteBuffer Copy() 816 { 817 return Copy(0); 818 } 819 820 public ByteBuffer Copy(int startIndex) 821 { 822 if (buf == null) 823 { 824 return new ByteBuffer(16); 825 } 826 byte[] target = new byte[buf.Length - startIndex]; 827 Array.Copy(buf, startIndex, target, 0, target.Length); 828 ByteBuffer buffer = new ByteBuffer(target.Length); 829 buffer.WriteBytes(target); 830 return buffer; 831 } 832 }
Of course, there are no ByteBuffer s in c#, but there are also ways to splice byte arrays, such as
1 void Send(byte[] data) 2 { 3 byte[] bytes = new byte[data.Length + 4]; 4 byte[] length = BitConverter.GetBytes(4); 5 //Because network byte order is always used for communication between different systems, while network byte order is large endpoint 6 //however c#Small endings are used, so the endings need to be converted here. As for the definition of endings, you can check them on the Internet by yourself, not to mention here 7 if (BitConverter.IsLittleEndian) 8 Array.Reverse(length); 9 Array.Copy(length, 0, bytes, 0, 4); 10 Array.Copy(data, 0, bytes, 4, data.Length); 11 mSocket.Send(bytes);12 }
Once the byte arrays are stitched together, they can be sent using the socket send method, but this article will continue with the processing of the received data.
The order in which data is received is to receive the message length first and then receive the message of a specified length based on the message length
1 void ReceiveMessage() 2 { 3 //As mentioned above, a complete message is the message length+Message Content 4 //So first create a 4-byte array to receive the message length 5 byte[] recvBytesHead = GetBytesReceive(4); 6 //Convert message length byte group to int numerical value 7 int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, 0)); 8 //Receives a byte group of a specified length based on the length of the message, which is the complete message content 9 byte[] recvBytesBody = GetBytesReceive(bodyLength); 10 //The content of the last deserialized message 11 IExtensible message = ProtobufSerilizer.DeSerialize<LoginRes>(messageBody); 12 }
The GetBytesRecive method receives messages and solves the problem of sticking and fewer packages, as follows
1 /// <summary> 2 /// Receive data and process 3 /// </summary> 4 /// <param name="length"></param> 5 /// <returns></returns> 6 byte[] GetBytesReceive(int length) 7 { 8 //Create a byte group of the specified length 9 byte[] recvBytes = new byte[length]; 10 //Set the maximum length of each received packet to 1024 bytes 11 int packageMaxLength = 1024; 12 //Use loops to ensure that the received data is complete, and if the remaining length is greater than 0, the receive is incomplete 13 while (length > 0) 14 { 15 //Create byte groups to hold byte streams that need to be received 16 byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength]; 17 int iBytesBody = 0; 18 //Set the length of the received data based on the remaining received length 19 if (length >= receiveBytes.Length) 20 iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, 0); 21 else 22 iBytesBody = mSocket.Receive(receiveBytes, length, 0); 23 receiveBytes.CopyTo(recvBytes, recvBytes.Length - length); 24 //Subtract received length 25 length -= iBytesBody; 26 } 27 return recvBytes; 28 }
At this point, the simple sending and receiving of messages is basically completed, but in the actual project, there will certainly not be only one message number. If it is a long-Link project, it needs to receive and send messages directly. What should we do?
As we all know, the display on unity's UI can only be executed in the main thread, but if we keep receiving and sending messages on the main thread, the experience will be very poor, so we have to open another thread to receive and send messages. The next one is to use multi-threading to complete socket communication.