socket传输protobuf字节流的实例详解
上一篇主要说的是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#中是没有的,源码摘至,不过作者并未在工具中添加获取所有字节码的方法,所以自己添加了一个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 ListByteBuffer>
pool = new ListByteBuffer>
();
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当然,c#中虽然没有ByteBuffer,但也有拼接字节数组的方法,比如
Send( [] bytes = [data.Length + [] length = BitConverter.GetBytes( Array.Copy(length, , bytes, , Array.Copy(data, , bytes, mSocket.Send(bytes);
}
字节数组拼接好后,就可以使用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.DeSerializeTest>
(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 else22 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通讯
以上就是socket传输protobuf字节流的实例详解的详细内容,更多请关注其它相关文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: socket传输protobuf字节流的实例详解
本文地址: https://pptw.com/jishu/592498.html
