socket transport protobuf byte stream by unity Explorer (2)

Keywords: C# socket network Java Unity

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.

Posted by thebluebus on Sun, 23 Jun 2019 10:02:10 -0700