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