版权声明:本文为原创文章,转载请声明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 }
当然,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通讯