unity探索者之socket傳輸protobuf位元組流(二)

来源:http://www.cnblogs.com/unityExplorer/archive/2017/06/10/6977935.html
-Advertisement-
Play Games

版權聲明:本文為原創文章,轉載請聲明http://www.cnblogs.com/unityExplorer/p/6977935.html 上一篇主要說的是protobuf位元組流的序列化和解析,將protobuf對象序列化為位元組流後雖然可以直接傳遞,但是實際在項目中卻不可能真的只是傳遞protobu ...


版權聲明:本文為原創文章,轉載請聲明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>	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 邏輯捲管理LVM 一 創建邏輯捲 1準備分區或硬碟 這裡使用/dev/sdb、/dev/sdc兩塊硬碟和/dev/sda9、/dev/sda10兩個分區,大小都為1G,磁碟有限,我也不想這麼摳的。 添加分區/dev/sda9、/dev/sda10 註意,要修改分區類型為Linux LVM 同樣的方法 ...
  • 本文快速分享一下快速零配置遷移 API 適配 iOS 對 IPv6 以及 HTTPS 的要求的方法,供大家參考。 <! more 零配置方案 最新的蘋果審核政策對 API 的 IPv6 以及 HTTPS 都作了要求,那麼如何快速進行適配呢? 這裡就快速給大家分享一個站點: "https://www. ...
  • 1.ngx_http_stub_status_module 是一個 Nginx 的內置 HTTP 模塊,該模塊可以提供 Nginx 的狀態信息。預設情況下這個模塊是不被編譯進來的,所以在編譯 Nginx 時要指定載入該模塊--with-http_stub_status_module 2.首先檢查ng ...
  • 本文介紹基於.net的證券公司宣傳微網站手機網頁的設計與實現方法。 隨著電腦技術的快速發展,基於Web的電腦網路金融、證券宣傳或交易網站已成為現代金融理財發展的熱點,B/S(Browser/Server)結構的互聯網宣傳也逐步在各大金融證券中得到了廣泛的應用[1]。互聯網金融(ITFIN)是指傳 ...
  • 第一種方法 string s=abcdeabcdeabcde; string[] sArray=s.Split('c') ; foreach(string i in sArray) Console.WriteLine(i.ToString()); 輸出下麵的結果: ab deab deab de 第 ...
  • 看過的相當不錯的一篇文章,但是對基架還時不太理解,大神們看到,希望指點一二,能告訴點學習資源就更好了! 本篇文章不是出自本人之手,轉載完全處於膜拜以及學習! 歡迎加我微信:jkxx123321 備註博客加就可以了! 最近由於需要在框架中提供一些自定義模板的功能,找到了一篇博客,可惜似乎是翻譯工具直接 ...
  • 一、前言 在日常的界面開發中,我們大多使用MVVM模式進行開發。通常情況下,一個PropertyGridControl或者DataGrid的ItemsSource設置好, 然後每一列綁定好某一條ItemsSource中的某一個欄位就可以跑起來了。 但是也有另一種情況: 假設一個界面Temp.xaml ...
  • 相對於WPF/Silverlight,UWP的動畫系統可以說有大幅提高,不過本文無意深入討論這些動畫API,本文將介紹使用Shape做一些進度、等待方面的動畫,除此之外也會介紹一些相關技巧。 1. 使用StrokeDashOffset做等待提示動畫 圓形的等待提示動畫十分容易做,只要讓它旋轉就可以了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...