在網絡傳輸的過程中 數據會比較多 處理起來會比較麻煩,因此得引入分層的概念,
計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決
“Any problem in computer science can be solved by anther layer of indirection.”
在客服端 和 服務器可以制訂一套通信協議, 通信協議是客服端和服務端 共同約定的一個 數據結構,其包含了雙方可以發送,並對方可以識別處理的數據包。
Protobuffer 可以把 一個類的實例序列化,和反序列化, 通過反射機制去取得, 開源的。
也支持JsonMap
github源碼
2019最新版是 3.7,需要.Net 4.5以上
API
protocolbuffer(以下簡稱PB)是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。google
提供了多種語言的實現:java、c#、c++、go 和
python,每一種實現都包含了相應語言的編譯器以及庫文件。由於它是一種二進制的格式,比使用 xml
進行數據交換快許多。可以把它用於分佈式應用之間的數據通信或者異構環境下的數據交換。作爲一種效率和兼容性都很優秀的二進制數據傳輸格式,可以用於諸如網絡傳輸、配置文件、數據存儲等諸多領域。
對一個數據類進行 序列化
項目要添加Pb的引用纔可以
using ProtoBuf;
using ProtoBuf.Meta;
[ProtoContract]
class NetMode
{
[ProtoMember(1)]
public int id;
[ProtoMember(2)]
public string name;
//如果要使用構造函數,那麼 默認構造函數 必須得存在,這個是一個小坑不然會報錯
public NetMode()
{
}
public NetMode(int id,string name)
{
this.id = id;
this.name = name;
}
}
另外如果有數據類有子類繼承, 需要在父類上添加特性 [ProtoInclude(number, typeof(子類名))]
爲啥呢?
因爲 這個 [ProtoContract] 特性 不允許子類繼承,也不允許 多個存在
如果 數據類不使用構造函數 則不會報錯
Untiy中其實也有序列化特性
不過 Pb 還加了反射技術,因此客服端服務器 兩邊的數據結構 其實是通用的,不然也用不了Pb,
[ProtoContract]特性 約定特性,可以用於 類 結構體,枚舉,接口
序列化對象
/// <summary>
/// 通過Protobuffer 序列化對象 返回byte[]數組
/// </summary>
/// <param name="value"></param>
/// <returns>二進制數據</returns>
public static byte[] PBSerialize(object value)
{
byte[] data = null;
//在範圍結束時 處理對象
using (MemoryStream ms=new MemoryStream())
{
if (value!=null)
{
// 序列化 到內存裏
Serializer.Serialize(ms, value); //此方法 會調用到以下方法. 實際上是同個方法,多層封裝
RuntimeTypeModel.Default.Serialize(ms, value);
}
//設置當前 位置
ms.Position = 0;
//寫入 數組
long length = ms.Length;
data = new byte[length];
//從內存流中讀取 寫入到buffer中
ms.Read(data, 0, (int)length);
}
return data;
}
public static byte[] Serialize(object obj)
{
using (var memory = new MemoryStream())
{
Serializer.Serialize(memory, obj);
return memory.ToArray();
}
}
序列化底層:
序列化方法, 使用 ProtoWriter 去寫進 內存裏
把一個null類型強轉爲 另一個 類型具體是?
序列化 核心部分
默認序列化內容。
獲取緩存區。
Interlocked.Exchange: 將對象設置爲指定值,並作爲原子操作返回對原始對象的引用
如果 原始對象的應用不爲null 就返回該緩衝區。
緩衝池子 大小是20
釋放 緩衝池
比較兩個對象以獲得引用相等性,如果它們相等,則替換其中一個對象、
檢查序列化狀態,序列化內容在使用後不能更改,如果使用就拋出 throw new InvalidOperationException(“The serialization-context cannot be changed once it is in use”);異常。
反序列化方法
public static T Deserialize<T>(byte[] data)
{
using (var memory = new MemoryStream(data))
return Serializer.Deserialize<T>(memory);
}
public static T Deserialize<T>(byte[] data, int offset, int size)
{
using (var memory = new MemoryStream(data, offset, size))
return Serializer.Deserialize<T>(memory);
}
public static object PBDSerialize( byte[] value ,Type type)
{
object o = null;
using (var memory=new MemoryStream(value))
{
o= RuntimeTypeModel.Default.Deserialize(memory,null, type);
}
return o;
}
///
/// 一個類 上的特性上的 編號 都不能重複 ,0 被視爲不是int ,0 會報錯
///
[ProtoInclude(3,typeof(NetModeSub))]
[ProtoInclude(3,typeof(NetModeSub))]
/// <summary>
/// 一個類 上的特性上的 編號 都不能重複 0 不是int 0 會報錯
/// </summary>
[ProtoInclude(3,typeof(NetModeSub))]
[ProtoContract]
class NetMode
{
[ProtoMember(1)]
public int id;
[ProtoMember(2)]
public string name;
public NetMode()
{
}
public NetMode(int id,string name)
{
this.id = id;
this.name = name;
}
}
[ProtoContract]
class NetModeSub:NetMode
{
[ProtoMember(2)] public float range;
/// <summary>
/// 子類的構造函數也一定要有,不然也會報錯
/// </summary>
public NetModeSub()
{
range = 12;
}
public NetModeSub(float range)
{
this.range = range;
}
public NetModeSub(int id, string name, float range) : base(id, name)
{
this.range = range;
}
}
另外類似的還有 結構體 枚舉 等,使用的 時候 都需要添加上 [ProtoContract] 特性, 其中的數組也要加上 [ProtoMember(1)], 如果不添加 [ProtoMember]特性,則 不會把值 序列化,相當於 是默認值。
以下是一個結構體 的用法,和上面 數據類用法的一致 的。
[ProtoContract]
public struct Vector3
{
/// <summary>
/// 結構體中 需要序列化的數組 都需要 加上 ProtoMember 特性, 否則 序列化後再 反序列化數值 會 丟失,變成默認值;
/// </summary>
[ProtoMember(1)]
public float x;
[ProtoMember(2)]
public float y;
[ProtoMember(3)]
public float z;
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public override string ToString()
{
return string.Format("({0},{1},{2})", x, y, z);
}
public float this[int index]
{
get
{
switch (index)
{
case 0:
return this.x;
case 1:
return this.y;
case 2:
return this.z;
default:
throw new IndexOutOfRangeException("Invalid Vector3 index!");
}
}
set
{
switch (index)
{
case 0:
this.x = value;
break;
case 1:
this.y = value;
break;
case 2:
this.z = value;
break;
default:
throw new IndexOutOfRangeException("Invalid Vector3 index!");
}
}
}
}
枚舉的處理:
這裏的ProtoContract 可以不用加。
https://github.com/qq21/ProtobufLearning
說說常見問題: 一個數據類裏的 聲明一個V3的結構體,並且給了它默認值 (1,2,3)
它的構造函數,
然後在 使用它的時候,
這個時候 序列化前 給它賦了下值,(0,0,0)
但是在序列化後,它的值就變成了最初始的值的, 即在數據類裏的初始值。
可以這麼理解,原始數據就是(1,2,3) 當 值變成0的時候,就是該原始數據的起點。
也就是默認值。