unity探索者之socket传输protobuf字节流

Posted 半颗星辰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity探索者之socket传输protobuf字节流相关的知识,希望对你有一定的参考价值。

上一篇主要说的是protobuf字节流的序列化和解析,将protobuf对象序列化为字节流后虽然可以直接传递,但是实际在项目中却不可能真的只是传递protobuf字节流,因为socket的tcp通讯中会出现几个很常见的问题,就是粘包和少包。所谓粘包,简单点说就是socket会将多个较小的包合并到一起发送。因为tcp是面向连接的,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。少包则是指缓存区满后,soket将不完整的包发送到接收端(按我的理解,粘包和少包其实是一个问题)。这样接收端一次接收到的数据就有可能是多个包,为了解决这个问题,在发送数据之前,需要将包的长度也发送出去。于是,包的结构就应该是 消息长度+消息内容。

这一篇,就来说说数据的拼接,干货来了

首先的拼接数据包

 1     /// <summary>
 2     /// 构建消息数据包
 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     }

代码中使用的ByteBuffer工具java中有提供,但是c#中是没有的,源码摘至https://www.oschina.net/code/snippet_42170_37516,不过作者并未在工具中添加获取所有字节码的方法,所以自己添加了一个GetBytes()方法

技术分享
  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,则返回值为8
100     /// </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         else
603         {
604             capacity = 0;
605             buf = null;
606         }
607     }
608 
609     /// <summary>
610     /// 设置/获取读指针位置
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     /// 设置/获取写指针位置
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     /// 标记读取的索引位置
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 LengthType
706     {
707         //byte类型
708         BYTE,
709         //short类型
710         SHORT,
711         //int类型
712         INT
713     }
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或byte
738     /// </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         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     /// 读取一个字符串
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

当然,c#中虽然没有ByteBuffer,但也有拼接字节数组的方法,比如

 1     void Send(byte[] data)
 2     {
 3         byte[] bytes = new byte[data.Length + 4];
 4         byte[] length = BitConverter.GetBytes(4);
 5         //因为不同系统间通信一律采用网络字节序,而网络字节序为大端序
 6         //但是c#中使用的是小端序,所以此处需要将端序转换下,关于端序的定义,大家可以自己上网查查,此处就不多说了
 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     }

字节数组拼接好后,就可以使用socket的send方法发送了,不过这一篇先继续讲完接收数据的处理

接收数据的顺序是先接收消息长度,然后根据消息长度接收指定长度的消息

 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           IExtensible message = ProtobufSerilizer.DeSerialize<LoginRes>(messageBody);
12     }

GetBytesRecive方法用于接收消息,并解决粘包、少包的问题,代码如下

 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             else
22                 iBytesBody = mSocket.Receive(receiveBytes, length, 0);
23             receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);
24             //减去已接收的长度
25             length -= iBytesBody;
26         }
27         return recvBytes;
28     }

到这里,消息的简单发送和接收就基本搞定了,但是,实际项目中,我们的消息数量肯定不会只有一条,如果是长链接的项目,更是需要一直接收和发送消息,该怎么办?

众所周知,unity的UI上的显示只能在主线程中执行,可是如果我们在主线程一直接收和发送消息,那体验将会极差,所以我们必须另外开启线程来负责消息的接收和发送,下一篇就是使用多线程来完成socket通讯

 

以上是关于unity探索者之socket传输protobuf字节流的主要内容,如果未能解决你的问题,请参考以下文章

unity探索者之socket传输protobuf字节流

unity探索者之socket传输protobuf字节流

Unity之C#端使用protobuf

Unity3d 学习路线-网络传输协议protobuf

细说http - tcp - udp - socket - protobuf协议

ProtoEditor - 如何在Unity中实现一个Protobuf通信协议类编辑器