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