引入第三方類庫
protobuf-net(可以通過nuget方式)
github地址:https://github.com/mgravell/protobuf-net
准備POCO
poco的生成方式有兩種
1.直接創建.proto文件然后通過protoGen(軟件)生成.cs文件
2.直接創建poco類 然后通過特性標簽進行標注.如下:
[ProtoBuf.ProtoContract]
class Student{
[ProtoBuf.ProtoMember(1)]
public string Name{get;set;}
[ProtoBuf.ProtoMember(2)]
public long Age{get;set;}
}
序列化對象
public static byte[] Serialize<T>(T t)
{
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<T>(ms, t);
return ms.ToArray();
}
}
public static T DeSerialize<T>(byte [] bytes)
{
using (MemoryStream ms = new MemoryStream(bytes))
{
T t = Serializer.Deserialize<T>(ms);
return t;
}
}
webapi 中的應用(.net freamwork 非 .net core)
web api使用application/x-protobuf協議時會報
所有需要添加這個mime協議的支持
參考這個項目:https://github.com/damienbod/WebApi2WebApiContribProtobuf
對ProtoBufFormatter進行了一些修改 具體的可以根據自己的業務特性修改下面這段代碼
/// <summary>
/// 處理MIME協議為application/x-protobuf 類
/// 注:在.net core 不需要特別添加對application/x-protobuf協議的支持
/// .net freamwork 若不加此協議支持網絡請求的時候會→遠程服務器異常:(415) Unsupported Media Type
/// </summary>
public class ProtoBufFormatter : MediaTypeFormatter
{
private static readonly MediaTypeHeaderValue mediatype = new MediaTypeHeaderValue("application/x-protobuf");
private static Lazy<RuntimeTypeModel> model = new Lazy<RuntimeTypeModel>(CreateTypeModel);
public static RuntimeTypeModel Model
{
get { return model.Value; }
}
public ProtoBufFormatter()
{
SupportedMediaTypes.Add(mediatype);
}
#region 判斷當前數據類型是否支持 protobuf格式
/**反序列化的時候在接口中進行所有直接返回ture即可
* 序列化的時候需要判斷對象時候能進行序列化 接口中直接返回對象即可(無需進行序列化)
*/
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return CanType(type);
}
//判斷對象是否被ProtoContractAttribute 特性進行標注(即是否能被序列化)
public static bool CanType(Type type)
{
bool iscan = type.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();
//增加類型是一個集合的判斷
if (!iscan && typeof(IEnumerable).IsAssignableFrom(type))
{
var temptype=type.GetGenericArguments().FirstOrDefault();
iscan=temptype.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();
}
return iscan;
}
#endregion
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
var tcs = new TaskCompletionSource<object>();
try
{
//object result = Model.Deserialize(stream, null, type);
int temp = -1;
List<byte> bytes = new List<byte>();
while ((temp = stream.ReadByte()) > 0)
{
bytes.Add((byte)temp);
}
tcs.SetResult(bytes.ToArray());
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
var tcs = new TaskCompletionSource<object>();
try
{
Model.Serialize(stream, value);
tcs.SetResult(null);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
private static RuntimeTypeModel CreateTypeModel()
{
var typeModel = TypeModel.Create();
typeModel.UseImplicitZeroDefaults = false;
return typeModel;
}
}
然后再config中對 ProtoBufFormatter 進行注冊
config.Formatters.Add(new ProtoBufFormatter())
遇到的問題
在使用protobuf進行通行utf-8編碼以后反序列是出現
這是因為utf8(1-8字節)編碼在對long(8字節 64位)編碼解碼后改變了他的大小
這里我們可以直接使用byte數組傳輸或者將編碼改為utf-16(2-4字節 16位) 但是這里會有一個問題就是大小有限制.