socket如何传输protobuf字节流
发表于:2024-11-22 作者:千家信息网编辑
千家信息网最后更新 2024年11月22日,小编给大家分享一下socket如何传输protobuf字节流,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!首先的拼接数据包
千家信息网最后更新 2024年11月22日socket如何传输protobuf字节流
小编给大家分享一下socket如何传输protobuf字节流,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
首先的拼接数据包
1 ///2 /// 构建消息数据包 3 /// 4 /// 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 ///5 /// 字节缓冲处理类,本类仅处理大字节序 6 /// 警告,本类非线程安全 7 /// 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 Listpool = new List (); 25 private static int poolMaxCount = 200; 26 //此对象是否池化 27 private bool isPool = false; 28 29 /// 30 /// 构造方法 31 /// 32 /// 初始容量 33 private ByteBuffer(int capacity) 34 { 35 buf = new byte[capacity]; 36 this.capacity = capacity; 37 } 38 39 ///40 /// 构造方法 41 /// 42 /// 初始字节数组 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 ///52 /// 构建一个capacity长度的字节缓存区ByteBuffer对象 53 /// 54 /// 初始容量 55 ///ByteBuffer对象 56 public static ByteBuffer Allocate(int capacity) 57 { 58 return new ByteBuffer(capacity); 59 } 60 61 ///62 /// 构建一个以bytes为字节缓存区的ByteBuffer对象,一般不推荐使用 63 /// 64 /// 初始字节数组 65 ///ByteBuffer对象 66 public static ByteBuffer Allocate(byte[] bytes) 67 { 68 return new ByteBuffer(bytes); 69 } 70 71 ///72 /// 获取一个池化的ByteBuffer对象,池化的对象必须在调用Dispose后才会推入池中,否则此方法等同于Allocate(int capacity)方法,此方法为线程安全的 73 /// 74 /// ByteBuffer对象的初始容量大小,如果缓存池中没有对象,则对象的容量大小为此值,否则为池中对象的实际容量值 75 ///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 /// 99 /// 根据length长度,确定大于此leng的最近的2次方数,如length=7,则返回值为8100 /// 101 /// 参考容量102 ///比参考容量大的最接近的2次方数 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 ///116 /// 翻转字节数组,如果本地字节序列为低字节序列,则进行翻转以转换为高字节序列117 /// 118 /// 待转为高字节序的字节数组119 ///高字节序列的字节数组 120 private byte[] flip(byte[] bytes)121 {122 if (BitConverter.IsLittleEndian)123 {124 Array.Reverse(bytes);125 }126 return bytes;127 }128 129 ///130 /// 确定内部字节缓存数组的大小131 /// 132 /// 当前容量133 /// 将来的容量134 ///将来的容量 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 ///155 /// 将bytes字节数组从startIndex开始的length字节写入到此缓存区156 /// 157 /// 待写入的字节数据158 /// 写入的开始位置159 /// 写入的长度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 ///175 /// 将字节数组中从0到length的元素写入缓存区176 /// 177 /// 待写入的字节数据178 /// 写入的长度179 public void WriteBytes(byte[] bytes, int length)180 {181 WriteBytes(bytes, 0, length);182 }183 184 ///185 /// 将字节数组全部写入缓存区186 /// 187 /// 待写入的字节数据188 public void WriteBytes(byte[] bytes)189 {190 WriteBytes(bytes, bytes.Length);191 }192 193 ///194 /// 将一个ByteBuffer的有效字节区写入此缓存区中195 /// 196 /// 待写入的字节缓存区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 ///205 /// 写入一个int16数据206 /// 207 /// short数据208 public void WriteShort(short value)209 {210 WriteBytes(flip(BitConverter.GetBytes(value)));211 }212 213 ///214 /// 写入一个ushort数据215 /// 216 /// ushort数据217 public void WriteUshort(ushort value)218 {219 WriteBytes(flip(BitConverter.GetBytes(value)));220 }221 222 ///223 /// 写入一个int32数据224 /// 225 /// int数据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 ///240 /// 写入一个uint32数据241 /// 242 /// uint数据243 public void WriteUint(uint value)244 {245 WriteBytes(flip(BitConverter.GetBytes(value)));246 }247 248 ///249 /// 写入一个int64数据250 /// 251 /// long数据252 public void WriteLong(long value)253 {254 WriteBytes(flip(BitConverter.GetBytes(value)));255 }256 257 ///258 /// 写入一个uint64数据259 /// 260 /// ulong数据261 public void WriteUlong(ulong value)262 {263 WriteBytes(flip(BitConverter.GetBytes(value)));264 }265 266 ///267 /// 写入一个float数据268 /// 269 /// float数据270 public void WriteFloat(float value)271 {272 WriteBytes(flip(BitConverter.GetBytes(value)));273 }274 275 ///276 /// 写入一个byte数据277 /// 278 /// byte数据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 ///289 /// 写入一个byte数据290 /// 291 /// byte数据292 public void WriteByte(int value)293 {294 byte b = (byte)value;295 WriteByte(b);296 }297 298 ///299 /// 写入一个double类型数据300 /// 301 /// double数据302 public void WriteDouble(double value)303 {304 WriteBytes(flip(BitConverter.GetBytes(value)));305 }306 307 ///308 /// 写入一个字符309 /// 310 /// 311 public void WriteChar(char value)312 {313 WriteBytes(flip(BitConverter.GetBytes(value)));314 }315 316 ///317 /// 写入一个布尔型数据318 /// 319 /// 320 public void WriteBoolean(bool value)321 {322 WriteBytes(flip(BitConverter.GetBytes(value)));323 }324 325 ///326 /// 读取一个字节327 /// 328 ///字节数据 329 public byte ReadByte()330 {331 byte b = buf[readIndex];332 readIndex++;333 return b;334 }335 336 ///337 /// 读取一个字节并转为int类型的数据338 /// 339 ///int数据 340 public int ReadByteToInt()341 {342 byte b = ReadByte();343 return (int)b;344 }345 346 ///347 /// 获取从index索引处开始len长度的字节348 /// 349 /// 350 /// 351 ///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 /// 360 /// 从读取索引位置开始读取len长度的字节数组361 /// 362 /// 待读取的字节长度363 ///字节数组 364 private byte[] Read(int len)365 {366 byte[] bytes = Get(readIndex, len);367 readIndex += len;368 return bytes;369 }370 371 ///372 /// 读取一个uint16数据373 /// 374 ///ushort数据 375 public ushort ReadUshort()376 {377 return BitConverter.ToUInt16(Read(2), 0);378 }379 380 ///381 /// 读取一个int16数据382 /// 383 ///short数据 384 public short ReadShort()385 {386 return BitConverter.ToInt16(Read(2), 0);387 }388 389 ///390 /// 读取一个uint32数据391 /// 392 ///uint数据 393 public uint ReadUint()394 {395 return BitConverter.ToUInt32(Read(4), 0);396 }397 398 ///399 /// 读取一个int32数据400 /// 401 ///int数据 402 public int ReadInt()403 {404 return BitConverter.ToInt32(Read(4), 0);405 }406 407 ///408 /// 读取一个uint64数据409 /// 410 ///ulong数据 411 public ulong ReadUlong()412 {413 return BitConverter.ToUInt64(Read(8), 0);414 }415 416 ///417 /// 读取一个long数据418 /// 419 ///long数据 420 public long ReadLong()421 {422 return BitConverter.ToInt64(Read(8), 0);423 }424 425 ///426 /// 读取一个float数据427 /// 428 ///float数据 429 public float ReadFloat()430 {431 return BitConverter.ToSingle(Read(4), 0);432 }433 434 ///435 /// 读取一个double数据436 /// 437 ///double数据 438 public double ReadDouble()439 {440 return BitConverter.ToDouble(Read(8), 0);441 }442 443 ///444 /// 读取一个字符445 /// 446 ///447 public char ReadChar()448 {449 return BitConverter.ToChar(Read(2), 0);450 }451 452 /// 453 /// 读取布尔型数据454 /// 455 ///456 public bool ReadBoolean()457 {458 return BitConverter.ToBoolean(Read(1), 0);459 }460 461 /// 462 /// 从读取索引位置开始读取len长度的字节到disbytes目标字节数组中463 /// 464 /// 读取的字节将存入此字节数组465 /// 目标字节数组的写入索引466 /// 读取的长度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 ///477 /// 获取一个字节478 /// 479 /// 480 ///481 public byte GetByte(int index)482 {483 return buf[index];484 }485 486 /// 487 /// 获取全部字节488 /// 489 ///490 public byte[] GetBytes()491 {492 return buf;493 }494 495 /// 496 /// 获取一个双精度浮点数据,不改变数据内容497 /// 498 /// 字节索引499 ///500 public double GetDouble(int index)501 {502 return BitConverter.ToDouble(Get(0, 8), 0);503 }504 505 /// 506 /// 获取一个浮点数据,不改变数据内容507 /// 508 /// 字节索引509 ///510 public float GetFloat(int index)511 {512 return BitConverter.ToSingle(Get(0, 4), 0);513 }514 515 /// 516 /// 获取一个长整形数据,不改变数据内容517 /// 518 /// 字节索引519 ///520 public long GetLong(int index)521 {522 return BitConverter.ToInt64(Get(0, 8), 0);523 }524 525 /// 526 /// 获取一个整形数据,不改变数据内容527 /// 528 /// 字节索引529 ///530 public int GetInt(int index)531 {532 return BitConverter.ToInt32(Get(0, 4), 0);533 }534 535 /// 536 /// 获取一个短整形数据,不改变数据内容537 /// 538 /// 字节索引539 ///540 public int GetShort(int index)541 {542 return BitConverter.ToInt16(Get(0, 2), 0);543 }544 545 546 /// 547 /// 清除已读字节并重建缓存区548 /// 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 ///571 /// 清空此对象,但保留字节缓存数组(空数组)572 /// 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 ///584 /// 释放对象,清除字节缓存数组,如果此对象为可池化,那么调用此方法将会把此对象推入到池中等待下次调用585 /// 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 ///610 /// 设置/获取读指针位置611 /// 612 public int ReaderIndex613 {614 get615 {616 return readIndex;617 }618 set619 {620 if (value < 0) return;621 readIndex = value;622 }623 }624 625 ///626 /// 设置/获取写指针位置627 /// 628 public int WriterIndex629 {630 get631 {632 return writeIndex;633 }634 set635 {636 if (value < 0) return;637 writeIndex = value;638 }639 }640 641 ///642 /// 标记读取的索引位置643 /// 644 public void MarkReaderIndex()645 {646 markReadIndex = readIndex;647 }648 649 ///650 /// 标记写入的索引位置651 /// 652 public void MarkWriterIndex()653 {654 markWirteIndex = writeIndex;655 }656 657 ///658 /// 将读取的索引位置重置为标记的读取索引位置659 /// 660 public void ResetReaderIndex()661 {662 readIndex = markReadIndex;663 }664 665 ///666 /// 将写入的索引位置重置为标记的写入索引位置667 /// 668 public void ResetWriterIndex()669 {670 writeIndex = markWirteIndex;671 }672 673 ///674 /// 可读的有效字节数675 /// 676 ///可读的字节数 677 public int ReadableBytes()678 {679 return writeIndex - readIndex;680 }681 682 ///683 /// 获取可读的字节数组684 /// 685 ///字节数据 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 ///694 /// 获取缓存区容量大小695 /// 696 ///缓存区容量 697 public int GetCapacity()698 {699 return this.capacity;700 }701 702 ///703 /// 简单的数据类型704 /// 705 public enum LengthType706 {707 //byte类型708 BYTE,709 //short类型710 SHORT,711 //int类型712 INT713 }714 715 ///716 /// 写入一个数据717 /// 718 /// 待写入的数据719 /// 待写入的数据类型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 ///737 /// 读取一个值,值类型根据type决定,int或short或byte738 /// 739 /// 值类型740 ///int数据 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 ///755 /// 写入一个字符串756 /// 757 /// 待写入的字符串758 /// 写入的字符串长度类型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 ///789 /// 读取一个字符串790 /// 791 /// 需读取的字符串长度792 ///字符串 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 ///801 /// 读取一个字符串802 /// 803 /// 字符串长度类型804 ///字符串 805 public string ReadUTF8String(LengthType lenType)806 {807 int len = ReadValue(lenType);808 return ReadUTF8String(len);809 }810 811 ///812 /// 复制一个对象,具有与原对象相同的数据,不改变原对象的数据813 /// 814 ///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.DeSerialize(messageBody);12 }
GetBytesRecive方法用于接收消息,并解决粘包、少包的问题,代码如下
1 ///2 /// 接收数据并处理 3 /// 4 /// 5 ///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如何传输protobuf字节流"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
数据
字节
长度
数组
对象
索引
消息
存区
位置
字符
容量
类型
内容
字符串
大小
方法
标记
线程
序列
缓存
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
圣堡动漫网络技术深圳有限公司
冰峰网络安全ppt
信息安全与网络技术哪个好
360网络安全武器
数据库系统有哪些功能
江苏小高考网络技术应用
一个人的网络技术
海康 存储服务器
递归关系 数据库
东南大学网络安全导师
网络安全人员素质
如东软件开发
网络安全短视频乱扫码危害大
网络安全手抄报咋画
软件开发不满意怎么办
辅助软件开发工具叫什么
比较公信力强的数据库
物流一图数据库设计
荣耀全明星手游进不去服务器
幼儿园安全风险隐患数据库
江夏订制软件开发多少钱
常用关系型数据库有哪些类型
冰峰网络安全ppt
软件开发活动的总体实施计划
我联互联网科技有限公司
网络安全与支付结算
数据库中班级编号怎么获得
中国电信校招网络技术工程师
保障网络安全是什么性工作
网络安全法海报