C# ProtobufGenBatTool v3 生成代码、方便、高效的encode/decode示例

工具前身:C# Protocol buffer 批处理工具
(该工具优化了经历了三个版本)

(其实还有V4版本,我还没上传,那个工具是重写后,更方便,现在在项目中使用,后面再上传)

现在这个版本就使用了Github上最新的:3.5.1版本(前两个版本都是:2.x的,无论是生成代码的速度、代码的运行效率,3.5.1都比2.x的高,IMessage取消了createBuilder/toBuilder,添加了:Parser,更加方便使用、更高效,该Protobuf版本的proto文件是使用proto3语法解释,部分proto1.x~2.x都不兼容了,具体看官方网吧,最主要的是protobuf3的protoc可以直接导出csharp代码了,之前2.x的是依靠一个:protogen的中间件来解析.protobin的方式,所以2.x生成csharp代码的效率大大降低了)

另附上Protocol buffer 官方网(需要科学上网),里面的教程、资源都最全面,需要的同学可看看
Protobuf encode(了解protobuf 是如何完成编码的也是很重要的,因为这个是protobuf的优点之一)

下载

下载工具(完整的工程)(我开发时使用的是VS2015,其他VS版本的同学自行处理哦)

ProtobufGenBatTool使用示例

  • -protoc_file 指定protoc.exe位置(相对、绝对路径都可)
  • -proto_file_inputpath 指定一个或多个包含.proto的文件目录,多个目录使用空格间隔开(相对、绝对路径都可)
  • -cs_proto_file_outputpath 指定.cs代码文件输出的目录(相对、绝对路径都可)
  • -cs_pid_map_file_output_path 指定协议与IMessage映射的类路径(相对、绝对路径都可)
  • -cs_pid_map_cls_namespace 指定-cs_pid_map_file_output_path 输出的类的namespace
  • -pause_on_complete 是否需要在运行后自动停止等待命令行输入而结束(可设置为:true或false,默认是:true,即:执行完后,会暂停等待Console输入即可结束)

ProtoMapExample的使用示例

        // 以下代码中的ProtoMap、Data1、等类都是由ProtobufGenBatTool生成的代码
        static void usage()
        {
            // global parser
            var parser = new Parser();

            var d = new Data1() { Name = "test", Age = 18 };

            // encode
            var msgBuffer = parser.toMsgBuffer(d);
            // decode method1:
            var copy2 = parser.toMsg(ProtoMap.getPID(), msgBuffer);

            d.Age = 20;
            d.Name = "tony";
            // encode
            msgBuffer = parser.toMsgBuffer(d);
            // decode method2:
            var copy3 = parser.toMsg(ProtoMap.getPID(typeof(Data1)), msgBuffer);
            // decode method3:
            var copy4 = parser.toMsg(msgBuffer);

            // dispose global parser
            parser.Dispose();

            Console.WriteLine(d.ToString() + copy2 + copy3 + copy4);
            /*
            Console:
            { "name": "tony", "age": 20 }{ "name": "test", "age": 18 }{ "name": "tony", "age": 20 }{ "name": "tony", "age": 20 }
            Press any key to exit.
             * */
        }

整体的使用就是这么简单

如果要遍历Message字段,在proto3版本中也是很简单的(proto2的话,我就不想说了)

void test()
{
    // 将上面的字段数据都打印一下:序号、名称、值
    PrintMessage(d);
    PrintMessage(copy2);
    PrintMessage(copy3);
    PrintMessage(copy4);
    /*
    Console:
    Field 1 (name): tony
    Field 2 (age): 20
    Field 1 (name): test
    Field 2 (age): 18
    Field 1 (name): tony
    Field 2 (age): 20
    Field 1 (name): tony
    Field 2 (age): 20
    */
}

public static void PrintMessage(IMessage message)
{
    var descriptor = message.Descriptor;
    foreach (var field in descriptor.Fields.InDeclarationOrder())
    {
        Console.WriteLine(
            "Field {0} ({1}): {2}",
            field.FieldNumber,
            field.Name,
            field.Accessor.GetValue(message));
    }
}

相关的类有
ProtoMap.cs类的代码,该类是由:ProtobufGenBatTool工具生成

// Generated by the protocol buffer ProtobufGenBatTool.  DO NOT EDIT!
using System;
using System.Collections.Generic;
using Google.Protobuf;
using com.net.protobuf.msg;

namespace com.net.protobuf.protomap
{
    public static class ProtoMap
    {
        public static readonly Dictionary<int, Type> _s_pMapper1 = new Dictionary<int, Type>();
        public static readonly Dictionaryint> _s_pMapper2 = new Dictionaryint>();
        public static readonly Dictionary<int, MessageParser> _s_pMapper3 = new Dictionary<int, MessageParser>();

        static ProtoMap()
        {
            _s_pMapper1[ 1 ] = typeof(Data1);
            _s_pMapper1[ 2 ] = typeof(MyData);
            _s_pMapper1[ 3 ] = typeof(MyRequest);
            _s_pMapper1[ 4 ] = typeof(MyResponse);
            _s_pMapper1[ 5 ] = typeof(Test);
            _s_pMapper1[ 6 ] = typeof(DemoData);
            _s_pMapper1[ 7 ] = typeof(DemoMyData);
            _s_pMapper1[ 8 ] = typeof(DemoMyRequest);
            _s_pMapper1[ 9 ] = typeof(DemoMyResponse);
            _s_pMapper1[ 10 ] = typeof(DemoTest);
            _s_pMapper1[ 11 ] = typeof(MPDemoTest);
            _s_pMapper1[ 12 ] = typeof(MPDemoTest1);
            _s_pMapper1[ 13 ] = typeof(MPDemoTest2);

            _s_pMapper2[ typeof(Data1) ] = 1;
            _s_pMapper2[ typeof(MyData) ] = 2;
            _s_pMapper2[ typeof(MyRequest) ] = 3;
            _s_pMapper2[ typeof(MyResponse) ] = 4;
            _s_pMapper2[ typeof(Test) ] = 5;
            _s_pMapper2[ typeof(DemoData) ] = 6;
            _s_pMapper2[ typeof(DemoMyData) ] = 7;
            _s_pMapper2[ typeof(DemoMyRequest) ] = 8;
            _s_pMapper2[ typeof(DemoMyResponse) ] = 9;
            _s_pMapper2[ typeof(DemoTest) ] = 10;
            _s_pMapper2[ typeof(MPDemoTest) ] = 11;
            _s_pMapper2[ typeof(MPDemoTest1) ] = 12;
            _s_pMapper2[ typeof(MPDemoTest2) ] = 13;

            _s_pMapper3[ 1 ] = Data1.Parser;
            _s_pMapper3[ 2 ] = MyData.Parser;
            _s_pMapper3[ 3 ] = MyRequest.Parser;
            _s_pMapper3[ 4 ] = MyResponse.Parser;
            _s_pMapper3[ 5 ] = Test.Parser;
            _s_pMapper3[ 6 ] = DemoData.Parser;
            _s_pMapper3[ 7 ] = DemoMyData.Parser;
            _s_pMapper3[ 8 ] = DemoMyRequest.Parser;
            _s_pMapper3[ 9 ] = DemoMyResponse.Parser;
            _s_pMapper3[ 10 ] = DemoTest.Parser;
            _s_pMapper3[ 11 ] = MPDemoTest.Parser;
            _s_pMapper3[ 12 ] = MPDemoTest1.Parser;
            _s_pMapper3[ 13 ] = MPDemoTest2.Parser;
        }

        public static Type getType(int pid)
        {
            return _s_pMapper1[pid];
        }

        public static int getPID(Type type)
        {
            return _s_pMapper2[type];
        }

        public static int getPID()
        {
            return _s_pMapper2[typeof(T)];
        }

        public static MessageParser getParser(int pid)
        {
            return _s_pMapper3[pid];
        }
    }
}

其他Parser编解码需要的类:

    /// 
    /// Parser for encode/deocde IMessage
    /// author  :   Jave.Lin
    /// date    :   2018-03-19
    /// 
    class Parser : IDisposable
    {
        private MsgOutStream _m_pMsgOutStream;
        private MemoryStream _m_pMs;

        private bool _m_bDisposed;

        public bool bDisposed { get { return _m_bDisposed; } }

        public Parser() : this(null) { }

        public Parser(MemoryStream pMs)
        {
            _m_pMs = _m_pMs ?? new MemoryStream();
            _m_pMsgOutStream = new MsgOutStream(_m_pMs);
        }

        public void Dispose()
        {
            _m_bDisposed = true;
            if (_m_pMsgOutStream != null)
            {
                _m_pMsgOutStream.Dispose();
                _m_pMsgOutStream = null;
            }
            if (_m_pMs != null)
            {
                _m_pMs.Dispose();
                _m_pMs = null;
            }
        }

        public IMessage toMsg(int pid, MsgOutStream stream)
        {
            return toMsg(pid, stream.getBuffer(), 0, stream.getLen());
        }
        public IMessage toMsg(int pid, MsgBuffer msgBuffer)
        {
            return ProtoMap.getParser(pid).ParseFrom(msgBuffer.buffer, msgBuffer.idx, msgBuffer.len);
        }
        public IMessage toMsg(int pid, byte[] bytes, int idx, int len)
        {
            return ProtoMap.getParser(pid).ParseFrom(bytes, idx, len);
        }
        public IMessage toMsg(MsgBuffer msgBuffer) where T : IMessage
        {
            var pid = ProtoMap.getPID ();
            return toMsg(pid, msgBuffer);
        }
        public IMessage toMsg(byte[] bytes, int idx, int len)
        {
            var pos = idx;
            var pkLen = BitConverter.ToInt16(bytes, pos);
            pos += 2;

            var pid = BitConverter.ToInt32(bytes, pos);
            pos += 4;

            return toMsg(pid, bytes, pos, pkLen);
        }

        public MsgBuffer toMsgBuffer(IMessage msg)
        {
            _m_pMsgOutStream.clear();
            _m_pMsgOutStream.push(msg);
            return new MsgBuffer { buffer = _m_pMsgOutStream.getBuffer(), idx = 0, len = _m_pMsgOutStream.getLen() };
        }

    }

    struct MsgBuffer
    {
        public byte[] buffer;
        public int idx;
        public int len;
    }

    class MsgOutStream : IDisposable
    {
        private CodedOutputStream _m_pCodeStream;
        private MemoryStream _m_pMemoryStream;
        private long _m_lLen;

        private bool _m_bDisposed;
        private bool _m_bOwnerd;

        private object locker = new object();

        public long lLen { get { return _m_lLen; } }
        public bool bDisposed { get { return _m_bDisposed; } }

        public MsgOutStream(MemoryStream pMemoryStream)
        {
            _m_pMemoryStream = pMemoryStream;
            if (_m_pMemoryStream == null)
            {
                _m_pMemoryStream = new MemoryStream();
                _m_bOwnerd = true;
            }
            _m_pCodeStream = new CodedOutputStream(_m_pMemoryStream, !_m_bOwnerd);
        }
        public void Dispose()
        {
            _m_bDisposed = true;

            if (_m_pCodeStream != null)
            {
                _m_pCodeStream.Dispose();
                _m_pCodeStream = null;
            }
            if (_m_bOwnerd)
            {
                if (_m_pMemoryStream != null)
                {
                    _m_pMemoryStream.Dispose();
                    _m_pMemoryStream = null;
                }
            }
        }

        public void push(IMessage msg)
        {
            lock (locker)
            {
                msg.WriteTo(_m_pCodeStream);
                _m_pCodeStream.Flush();
                _m_lLen = _m_pMemoryStream.Position;
            }
        }

        public void clear()
        {
            lock (locker)
            {
                _m_lLen = 0;
                _m_pMemoryStream.Position = 0;
            }
        }

        public byte[] getBuffer()
        {
            lock (locker)
            {
                return _m_pMemoryStream.GetBuffer();
            }
        }

        public int getLen()
        {
            return (int)_m_lLen;
        }
    }

你可能感兴趣的:(C#,Protobuf,Protobuf3,协议映射序列化)