zoukankan      html  css  js  c++  java
  • 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通讯

    由于环境不同关系,并非所有的博客内容都会上传完整的源码,大部分的源码,大家可以到我的github主页上的UGCFramework查找

    传送门:https://github.com/wulonghao/UGCFramework
  • 相关阅读:
    软件架构阅读笔记04
    软件架构阅读笔记03
    TortoiseGit和intellij idea配置秘钥
    linux关闭在线登录用户
    汉化gitlab
    GitLab服务器搭建
    redis 中如何切换db
    弹性伸缩问题
    Filebeat+Logstash自定义多索引
    logstash
  • 原文地址:https://www.cnblogs.com/unityExplorer/p/6977935.html
Copyright © 2011-2022 走看看