unity探索者之socket傳輸protobuf字節流(二)


版權聲明:本文為原創文章,轉載請聲明http://www.cnblogs.com/unityExplorer/p/6977935.html 

上一篇主要說的是protobuf字節流的序列化和解析,將protobuf對象序列化為字節流后雖然可以直接傳遞,但是實際在項目中卻不可能真的只是傳遞protobuf字節流,因為socket的tcp通訊中會出現幾個很常見的問題,就是粘包和少包。所謂粘包,簡單點說就是socket會將多個較小的包合並到一起發送。因為tcp是面向連接的,發送端為了將多個發往接收端的包,更有效的發到對方,使用了優化方法(Nagle算法),將多次間隔較小且數據量小的數據,合並成一個大的數據塊,然后進行封包。少包則是指緩存區滿后,soket將不完整的包發送到接收端(按我的理解,粘包和少包其實是一個問題)。這樣接收端一次接收到的數據就有可能是多個包,為了解決這個問題,在發送數據之前,需要將包的長度也發送出去。於是,包的結構就應該是 消息長度+消息內容。

這一篇,就來說說數據的拼接,干貨來了

首先的拼接數據包

 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工具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           Test message = ProtobufSerilizer.DeSerialize<Test>(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通訊

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM