Home  >  Article  >  Backend Development  >  Detailed explanation of examples of socket transmission protobuf byte stream

Detailed explanation of examples of socket transmission protobuf byte stream

零下一度
零下一度Original
2017-06-23 15:56:054243browse

Copyright Statement: This article is an original article, please declare when reprinting

The previous article mainly talks about the serialization and parsing of protobuf byte stream, serializing protobuf objects into words Although it can be transmitted directly after throttling, it is actually impossible to transmit only the protobuf byte stream in the project, because there are several very common problems in the TCP communication of the socket, namely sticky packets and missing packets. The so-called sticky packets simply mean that the socket will merge multiple smaller packets and send them together. Because TCP is connection-oriented, in order to send multiple packets to the receiving end more efficiently, the sending end uses an optimization method (Nagle algorithm) to merge multiple data with small intervals and small data volume into A large data block is then packetized. Missing packets means that after the buffer area is full, soket will send incomplete packets to the receiving end (according to my understanding, sticky packets and missing packets are actually a problem). In this way, the data received by the receiving end at one time may be multiple packets. In order to solve this problem, the length of the packet needs to be sent before sending the data. Therefore, the structure of the packet should be message length + message content.

This article, let’s talk about data splicing, here comes the useful information

First, splice the data packet

 1     /// <summary> 2     /// 构建消息数据包 3     /// </summary> 4     /// <param name="protobufModel"></param> 5     byte[] BuildPackage(IExtensible protobufModel) 6     { 7         if (protobufModel != null) 8         { 9             byte[] b = ProtobufSerilizer.Serialize(protobufModel);10 11             ByteBuffer buf = ByteBuffer.Allocate(b.Length + 4);12             buf.WriteInt(b.Length);13             buf.WriteBytes(b);14             return buf.GetBytes();15         }16         return null;17     }

ByteBuffer used in the code The tool is provided in Java, but not in C#. The source code is excerpted. However, the author did not add a method to obtain all bytecodes in the tool, so he added a GetBytes() method

  1 using System;  2 using System.Collections.Generic;  3   4 /// <summary>  5 /// 字节缓冲处理类,本类仅处理大字节序  6 /// 警告,本类非线程安全  7 /// </summary>  8 public class ByteBuffer  9 { 10     //字节缓存区 11     private byte[] buf; 12     //读取索引 13     private int readIndex = 0; 14     //写入索引 15     private int writeIndex = 0; 16     //读取索引标记 17     private int markReadIndex = 0; 18     //写入索引标记 19     private int markWirteIndex = 0; 20     //缓存区字节数组的长度 21     private int capacity; 22  23     //对象池 24     private static List<ByteBuffer> pool = new List<ByteBuffer>(); 25     private static int poolMaxCount = 200; 26     //此对象是否池化 27     private bool isPool = false; 28  29     /// <summary> 30     /// 构造方法 31     /// </summary> 32     /// <param name="capacity">初始容量</param> 33     private ByteBuffer(int capacity) 34     { 35         buf = new byte[capacity]; 36         this.capacity = capacity; 37     } 38  39     /// <summary> 40     /// 构造方法 41     /// </summary> 42     /// <param name="bytes">初始字节数组</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     /// 构建一个capacity长度的字节缓存区ByteBuffer对象 53     /// </summary> 54     /// <param name="capacity">初始容量</param> 55     /// <returns>ByteBuffer对象</returns> 56     public static ByteBuffer Allocate(int capacity) 57     { 58         return new ByteBuffer(capacity); 59     } 60  61     /// <summary> 62     /// 构建一个以bytes为字节缓存区的ByteBuffer对象,一般不推荐使用 63     /// </summary> 64     /// <param name="bytes">初始字节数组</param> 65     /// <returns>ByteBuffer对象</returns> 66     public static ByteBuffer Allocate(byte[] bytes) 67     { 68         return new ByteBuffer(bytes); 69     } 70  71     /// <summary> 72     /// 获取一个池化的ByteBuffer对象,池化的对象必须在调用Dispose后才会推入池中,否则此方法等同于Allocate(int capacity)方法,此方法为线程安全的 73     /// </summary> 74     /// <param name="capacity">ByteBuffer对象的初始容量大小,如果缓存池中没有对象,则对象的容量大小为此值,否则为池中对象的实际容量值</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     /// 根据length长度,确定大于此leng的最近的2次方数,如length=7,则返回值为8100     /// </summary>101     /// <param name="length">参考容量</param>102     /// <returns>比参考容量大的最接近的2次方数</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     /// 翻转字节数组,如果本地字节序列为低字节序列,则进行翻转以转换为高字节序列117     /// </summary>118     /// <param name="bytes">待转为高字节序的字节数组</param>119     /// <returns>高字节序列的字节数组</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     /// 确定内部字节缓存数组的大小131     /// </summary>132     /// <param name="currLen">当前容量</param>133     /// <param name="futureLen">将来的容量</param>134     /// <returns>将来的容量</returns>135     private int FixSizeAndReset(int currLen, int futureLen)136     {137         if (futureLen > currLen)138         {139             //以原大小的2次方数的两倍确定内部字节缓存区大小140             int size = FixLength(currLen) * 2;141             if (futureLen > size)142             {143                 //以将来的大小的2次方的两倍确定内部字节缓存区大小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     /// 将bytes字节数组从startIndex开始的length字节写入到此缓存区156     /// </summary>157     /// <param name="bytes">待写入的字节数据</param>158     /// <param name="startIndex">写入的开始位置</param>159     /// <param name="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     /// 将字节数组中从0到length的元素写入缓存区176     /// </summary>177     /// <param name="bytes">待写入的字节数据</param>178     /// <param name="length">写入的长度</param>179     public void WriteBytes(byte[] bytes, int length)180     {181         WriteBytes(bytes, 0, length);182     }183 184     /// <summary>185     /// 将字节数组全部写入缓存区186     /// </summary>187     /// <param name="bytes">待写入的字节数据</param>188     public void WriteBytes(byte[] bytes)189     {190         WriteBytes(bytes, bytes.Length);191     }192 193     /// <summary>194     /// 将一个ByteBuffer的有效字节区写入此缓存区中195     /// </summary>196     /// <param name="buffer">待写入的字节缓存区</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     /// 写入一个int16数据206     /// </summary>207     /// <param name="value">short数据</param>208     public void WriteShort(short value)209     {210         WriteBytes(flip(BitConverter.GetBytes(value)));211     }212 213     /// <summary>214     /// 写入一个ushort数据215     /// </summary>216     /// <param name="value">ushort数据</param>217     public void WriteUshort(ushort value)218     {219         WriteBytes(flip(BitConverter.GetBytes(value)));220     }221 222     /// <summary>223     /// 写入一个int32数据224     /// </summary>225     /// <param name="value">int数据</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     /// 写入一个uint32数据241     /// </summary>242     /// <param name="value">uint数据</param>243     public void WriteUint(uint value)244     {245         WriteBytes(flip(BitConverter.GetBytes(value)));246     }247 248     /// <summary>249     /// 写入一个int64数据250     /// </summary>251     /// <param name="value">long数据</param>252     public void WriteLong(long value)253     {254         WriteBytes(flip(BitConverter.GetBytes(value)));255     }256 257     /// <summary>258     /// 写入一个uint64数据259     /// </summary>260     /// <param name="value">ulong数据</param>261     public void WriteUlong(ulong value)262     {263         WriteBytes(flip(BitConverter.GetBytes(value)));264     }265 266     /// <summary>267     /// 写入一个float数据268     /// </summary>269     /// <param name="value">float数据</param>270     public void WriteFloat(float value)271     {272         WriteBytes(flip(BitConverter.GetBytes(value)));273     }274 275     /// <summary>276     /// 写入一个byte数据277     /// </summary>278     /// <param name="value">byte数据</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     /// 写入一个byte数据290     /// </summary>291     /// <param name="value">byte数据</param>292     public void WriteByte(int value)293     {294         byte b = (byte)value;295         WriteByte(b);296     }297 298     /// <summary>299     /// 写入一个double类型数据300     /// </summary>301     /// <param name="value">double数据</param>302     public void WriteDouble(double value)303     {304         WriteBytes(flip(BitConverter.GetBytes(value)));305     }306 307     /// <summary>308     /// 写入一个字符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     /// 写入一个布尔型数据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     /// 读取一个字节327     /// </summary>328     /// <returns>字节数据</returns>329     public byte ReadByte()330     {331         byte b = buf[readIndex];332         readIndex++;333         return b;334     }335 336     /// <summary>337     /// 读取一个字节并转为int类型的数据338     /// </summary>339     /// <returns>int数据</returns>340     public int ReadByteToInt()341     {342         byte b = ReadByte();343         return (int)b;344     }345 346     /// <summary>347     /// 获取从index索引处开始len长度的字节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     /// 从读取索引位置开始读取len长度的字节数组361     /// </summary>362     /// <param name="len">待读取的字节长度</param>363     /// <returns>字节数组</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     /// 读取一个uint16数据373     /// </summary>374     /// <returns>ushort数据</returns>375     public ushort ReadUshort()376     {377         return BitConverter.ToUInt16(Read(2), 0);378     }379 380     /// <summary>381     /// 读取一个int16数据382     /// </summary>383     /// <returns>short数据</returns>384     public short ReadShort()385     {386         return BitConverter.ToInt16(Read(2), 0);387     }388 389     /// <summary>390     /// 读取一个uint32数据391     /// </summary>392     /// <returns>uint数据</returns>393     public uint ReadUint()394     {395         return BitConverter.ToUInt32(Read(4), 0);396     }397 398     /// <summary>399     /// 读取一个int32数据400     /// </summary>401     /// <returns>int数据</returns>402     public int ReadInt()403     {404         return BitConverter.ToInt32(Read(4), 0);405     }406 407     /// <summary>408     /// 读取一个uint64数据409     /// </summary>410     /// <returns>ulong数据</returns>411     public ulong ReadUlong()412     {413         return BitConverter.ToUInt64(Read(8), 0);414     }415 416     /// <summary>417     /// 读取一个long数据418     /// </summary>419     /// <returns>long数据</returns>420     public long ReadLong()421     {422         return BitConverter.ToInt64(Read(8), 0);423     }424 425     /// <summary>426     /// 读取一个float数据427     /// </summary>428     /// <returns>float数据</returns>429     public float ReadFloat()430     {431         return BitConverter.ToSingle(Read(4), 0);432     }433 434     /// <summary>435     /// 读取一个double数据436     /// </summary>437     /// <returns>double数据</returns>438     public double ReadDouble()439     {440         return BitConverter.ToDouble(Read(8), 0);441     }442 443     /// <summary>444     /// 读取一个字符445     /// </summary>446     /// <returns></returns>447     public char ReadChar()448     {449         return BitConverter.ToChar(Read(2), 0);450     }451 452     /// <summary>453     /// 读取布尔型数据454     /// </summary>455     /// <returns></returns>456     public bool ReadBoolean()457     {458         return BitConverter.ToBoolean(Read(1), 0);459     }460 461     /// <summary>462     /// 从读取索引位置开始读取len长度的字节到disbytes目标字节数组中463     /// </summary>464     /// <param name="disbytes">读取的字节将存入此字节数组</param>465     /// <param name="disstart">目标字节数组的写入索引</param>466     /// <param name="len">读取的长度</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     /// 获取一个字节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     /// 获取全部字节488     /// </summary>489     /// <returns></returns>490     public byte[] GetBytes()491     {492         return buf;493     }494 495     /// <summary>496     /// 获取一个双精度浮点数据,不改变数据内容497     /// </summary>498     /// <param name="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     /// 获取一个浮点数据,不改变数据内容507     /// </summary>508     /// <param name="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     /// 获取一个长整形数据,不改变数据内容517     /// </summary>518     /// <param name="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     /// 获取一个整形数据,不改变数据内容527     /// </summary>528     /// <param name="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     /// 获取一个短整形数据,不改变数据内容537     /// </summary>538     /// <param name="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     /// 清除已读字节并重建缓存区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     /// 清空此对象,但保留字节缓存数组(空数组)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     /// 释放对象,清除字节缓存数组,如果此对象为可池化,那么调用此方法将会把此对象推入到池中等待下次调用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         else603         {604             capacity = 0;605             buf = null;606         }607     }608 609     /// <summary>610     /// 设置/获取读指针位置611     /// </summary>612     public int ReaderIndex613     {614         get615         {616             return readIndex;617         }618         set619         {620             if (value < 0) return;621             readIndex = value;622         }623     }624 625     /// <summary>626     /// 设置/获取写指针位置627     /// </summary>628     public int WriterIndex629     {630         get631         {632             return writeIndex;633         }634         set635         {636             if (value < 0) return;637             writeIndex = value;638         }639     }640 641     /// <summary>642     /// 标记读取的索引位置643     /// </summary>644     public void MarkReaderIndex()645     {646         markReadIndex = readIndex;647     }648 649     /// <summary>650     /// 标记写入的索引位置651     /// </summary>652     public void MarkWriterIndex()653     {654         markWirteIndex = writeIndex;655     }656 657     /// <summary>658     /// 将读取的索引位置重置为标记的读取索引位置659     /// </summary>660     public void ResetReaderIndex()661     {662         readIndex = markReadIndex;663     }664 665     /// <summary>666     /// 将写入的索引位置重置为标记的写入索引位置667     /// </summary>668     public void ResetWriterIndex()669     {670         writeIndex = markWirteIndex;671     }672 673     /// <summary>674     /// 可读的有效字节数675     /// </summary>676     /// <returns>可读的字节数</returns>677     public int ReadableBytes()678     {679         return writeIndex - readIndex;680     }681 682     /// <summary>683     /// 获取可读的字节数组684     /// </summary>685     /// <returns>字节数据</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     /// 获取缓存区容量大小695     /// </summary>696     /// <returns>缓存区容量</returns>697     public int GetCapacity()698     {699         return this.capacity;700     }701 702     /// <summary>703     /// 简单的数据类型704     /// </summary>705     public enum LengthType706     {707         //byte类型708         BYTE,709         //short类型710         SHORT,711         //int类型712         INT713     }714 715     /// <summary>716     /// 写入一个数据717     /// </summary>718     /// <param name="value">待写入的数据</param>719     /// <param name="type">待写入的数据类型</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     /// 读取一个值,值类型根据type决定,int或short或byte738     /// </summary>739     /// <param name="type">值类型</param>740     /// <returns>int数据</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     /// 写入一个字符串756     /// </summary>757     /// <param name="content">待写入的字符串</param>758     /// <param name="lenType">写入的字符串长度类型</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         else774         {775             WriteInt(bytes.Length);776             max = int.MaxValue;777         }778         if (bytes.Length > max)779         {780             WriteBytes(bytes, 0, max);781         }782         else783         {784             WriteBytes(bytes, 0, bytes.Length);785         }786     }787 788     /// <summary>789     /// 读取一个字符串790     /// </summary>791     /// <param name="len">需读取的字符串长度</param>792     /// <returns>字符串</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     /// 读取一个字符串802     /// </summary>803     /// <param name="lenType">字符串长度类型</param>804     /// <returns>字符串</returns>805     public string ReadUTF8String(LengthType lenType)806     {807         int len = ReadValue(lenType);808         return ReadUTF8String(len);809     }810 811     /// <summary>812     /// 复制一个对象,具有与原对象相同的数据,不改变原对象的数据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 }
View Code

Of course, although there is no ByteBuffer in c#, there are ways to splice byte arrays, such as

      Send(          [] bytes =  [data.Length +          [] length = BitConverter.GetBytes(                                     Array.Copy(length, , bytes, ,          Array.Copy(data, , bytes,          mSocket.Send(bytes);
     }

After the byte array is spliced, you can use the send method of the socket to send it, but this article will continue to finish the processing of receiving data.

The order of receiving data is to receive the message length first. , and then receive the message of the specified length according to the message length

 1     void ReceiveMessage() 2     { 3           //上文说过,一个完整的消息是 消息长度+消息内容 4           //所以先创建一个长度4的字节数组,用于接收消息长度 5           byte[] recvBytesHead = GetBytesReceive(4); 6           //将消息长度字节组转为int数值 7           int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, 0)); 8           //根据消息长度接收指定长度的字节组,这个字节组就是完整的消息内容 9           byte[] recvBytesBody = GetBytesReceive(bodyLength);10           //最后反序列化消息的内容11           Test message = ProtobufSerilizer.DeSerialize<Test>(messageBody);12     }

The GetBytesRecive method is used to receive the message and solve the problem of sticky packets and missing packets. The code is as follows

 1     /// <summary> 2     /// 接收数据并处理 3     /// </summary> 4     /// <param name="length"></param> 5     /// <returns></returns> 6     byte[] GetBytesReceive(int length) 7     { 8         //创建指定长度的字节组 9         byte[] recvBytes = new byte[length];10         //设置每次接收包的最大长度为1024个字节11         int packageMaxLength = 1024;12         //使用循环来保证接收的数据是完整的,如果剩余长度大于0,证明接收未完成13         while (length > 0)14         {15             //创建字节组,用于存放需要接收的字节流16             byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength];17             int iBytesBody = 0;18             //根据剩余需接收的长度来设置接收数据的长度19             if (length >= receiveBytes.Length)20                 iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, 0);21             else22                 iBytesBody = mSocket.Receive(receiveBytes, length, 0);23             receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);24             //减去已接收的长度25             length -= iBytesBody;26         }27         return recvBytes;28     }

At this point, the simple sending and receiving of messages is basically done. However, in actual projects, we will definitely not have only one message. If it is a long-link project, we need to receive and send messages all the time. ,what to do?

As we all know, the display on Unity's UI can only be executed in the main thread. However, if we keep receiving and sending messages in the main thread, the experience will be extremely poor, so we must open another thread to be responsible for the messages. Receiving and sending, the next article is to use multi-threading to complete socket communication

The above is the detailed content of Detailed explanation of examples of socket transmission protobuf byte stream. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn