C# 使用Protobuf通信


引入第三方類庫

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位) 但是這里會有一個問題就是大小有限制.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM