• Protobuf-net学习笔记


    对于Socket应用来说,如何序列化和反序列化消息一直是比较头痛的问题,C#提供了自动序列化的功能(类似AS3中的AMF),但是唯一的缺点就是前后端都必须是C#实现,如果前后端语言不一致该怎么办?

    Google的Protobuf很好的解决了这个问题,支持类似C++、Java等主流语言,但是官方版本未提供C#语言的实现,但是不用担心,有很多开发者已经帮助我们实现了C#的Protobuf,其中应用得最多的是Protobuf-net,下载地址是:http://code.google.com/p/protobuf-net/

    当然如果404了也不用担心,我提供了一个百度网盘的下载:http://pan.baidu.com/s/1o6qTFEa

    解压后即可使用,下面我们创建两个协议示例文件来展示一下Protobuf的用法,其中一个文件用到了另一个文件定义的消息体:

    test1.proto:

     1 //这里定义基础的消息体或枚举
     2 
     3 package Test.Base;
     4 
     5 //定义枚举
     6 
     7 enum Sex
     8 {
     9     MALE = 0;
    10     FEMALE = 1;
    11 }
    12 
    13 //定义消息体
    14 
    15 message People
    16 {
    17     required string name = 1;
    18     required Sex sex = 2;
    19     optional int32 age = 3;
    20 }
    21 
    22 message Hero
    23 {
    24     required People people = 1;
    25     optional string skill = 2;
    26 }

    test2.proto:

     1 //有用到 test1 的东西需要导入 test1
     2 
     3 import "test1.proto";
     4 
     5 //这里定义直接使用的消息体
     6 
     7 package Test.App;
     8 
     9 //定义消息体
    10 
    11 message SuperHero
    12 {
    13     required Test.Base.Hero hero = 1;
    14     required string superSkill = 2;
    15 }

    那么该如何生成可以使用的代码呢?

    一般通过命令行进行生成,但是如果每次修改了协议都敲一通肯定会累死人的,下面在test1.proto和test2.proto的同一目录下新建一个名为gen.cmd的文件,内容如下:

    1 D:project	oolprotobuf-netProtoGenprotogen.exe -i:test1.proto -i:test2.proto -o:test.cs
    2 pause

    当前protogen.exe的路径以你自己的为准,双击会在本目录下生成test.cs文件。

    生成的test.cs内容如下:

      1 //------------------------------------------------------------------------------
      2 // <auto-generated>
      3 //     This code was generated by a tool.
      4 //
      5 //     Changes to this file may cause incorrect behavior and will be lost if
      6 //     the code is regenerated.
      7 // </auto-generated>
      8 //------------------------------------------------------------------------------
      9 
     10 // Generated from: test1.proto
     11 namespace Test.Base
     12 {
     13   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"People")]
     14   public partial class People : global::ProtoBuf.IExtensible
     15   {
     16     public People() {}
     17     
     18     private string _name;
     19     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"name", DataFormat = global::ProtoBuf.DataFormat.Default)]
     20     public string name
     21     {
     22       get { return _name; }
     23       set { _name = value; }
     24     }
     25     private Test.Base.Sex _sex;
     26     [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"sex", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
     27     public Test.Base.Sex sex
     28     {
     29       get { return _sex; }
     30       set { _sex = value; }
     31     }
     32     private int _age = default(int);
     33     [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"age", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
     34     [global::System.ComponentModel.DefaultValue(default(int))]
     35     public int age
     36     {
     37       get { return _age; }
     38       set { _age = value; }
     39     }
     40     private global::ProtoBuf.IExtension extensionObject;
     41     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
     42       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
     43   }
     44   
     45   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Hero")]
     46   public partial class Hero : global::ProtoBuf.IExtensible
     47   {
     48     public Hero() {}
     49     
     50     private Test.Base.People _people;
     51     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"people", DataFormat = global::ProtoBuf.DataFormat.Default)]
     52     public Test.Base.People people
     53     {
     54       get { return _people; }
     55       set { _people = value; }
     56     }
     57     private string _skill = "";
     58     [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"skill", DataFormat = global::ProtoBuf.DataFormat.Default)]
     59     [global::System.ComponentModel.DefaultValue("")]
     60     public string skill
     61     {
     62       get { return _skill; }
     63       set { _skill = value; }
     64     }
     65     private global::ProtoBuf.IExtension extensionObject;
     66     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
     67       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
     68   }
     69   
     70     [global::ProtoBuf.ProtoContract(Name=@"Sex")]
     71     public enum Sex
     72     {
     73             
     74       [global::ProtoBuf.ProtoEnum(Name=@"MALE", Value=0)]
     75       MALE = 0,
     76             
     77       [global::ProtoBuf.ProtoEnum(Name=@"FEMALE", Value=1)]
     78       FEMALE = 1
     79     }
     80   
     81 }
     82 // Generated from: test2.proto
     83 // Note: requires additional types generated from: test1.proto
     84 namespace Test.App
     85 {
     86   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SuperHero")]
     87   public partial class SuperHero : global::ProtoBuf.IExtensible
     88   {
     89     public SuperHero() {}
     90     
     91     private Test.Base.Hero _hero;
     92     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"hero", DataFormat = global::ProtoBuf.DataFormat.Default)]
     93     public Test.Base.Hero hero
     94     {
     95       get { return _hero; }
     96       set { _hero = value; }
     97     }
     98     private string _superSkill;
     99     [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"superSkill", DataFormat = global::ProtoBuf.DataFormat.Default)]
    100     public string superSkill
    101     {
    102       get { return _superSkill; }
    103       set { _superSkill = value; }
    104     }
    105     private global::ProtoBuf.IExtension extensionObject;
    106     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
    107       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
    108   }
    109   
    110 }
    View Code

    当前如果要生成两个文件而非一个时可以这么写:

    1 D:project	oolprotobuf-netProtoGenprotogen.exe -i:test1.proto -o:test1.cs
    2 D:project	oolprotobuf-netProtoGenprotogen.exe -i:test2.proto -o:test2.cs
    3 pause

    生成的test1.cs:

     1 //------------------------------------------------------------------------------
     2 // <auto-generated>
     3 //     This code was generated by a tool.
     4 //
     5 //     Changes to this file may cause incorrect behavior and will be lost if
     6 //     the code is regenerated.
     7 // </auto-generated>
     8 //------------------------------------------------------------------------------
     9 
    10 // Generated from: test1.proto
    11 namespace Test.Base
    12 {
    13   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"People")]
    14   public partial class People : global::ProtoBuf.IExtensible
    15   {
    16     public People() {}
    17     
    18     private string _name;
    19     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"name", DataFormat = global::ProtoBuf.DataFormat.Default)]
    20     public string name
    21     {
    22       get { return _name; }
    23       set { _name = value; }
    24     }
    25     private Test.Base.Sex _sex;
    26     [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"sex", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
    27     public Test.Base.Sex sex
    28     {
    29       get { return _sex; }
    30       set { _sex = value; }
    31     }
    32     private int _age = default(int);
    33     [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"age", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
    34     [global::System.ComponentModel.DefaultValue(default(int))]
    35     public int age
    36     {
    37       get { return _age; }
    38       set { _age = value; }
    39     }
    40     private global::ProtoBuf.IExtension extensionObject;
    41     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
    42       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
    43   }
    44   
    45   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Hero")]
    46   public partial class Hero : global::ProtoBuf.IExtensible
    47   {
    48     public Hero() {}
    49     
    50     private Test.Base.People _people;
    51     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"people", DataFormat = global::ProtoBuf.DataFormat.Default)]
    52     public Test.Base.People people
    53     {
    54       get { return _people; }
    55       set { _people = value; }
    56     }
    57     private string _skill = "";
    58     [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"skill", DataFormat = global::ProtoBuf.DataFormat.Default)]
    59     [global::System.ComponentModel.DefaultValue("")]
    60     public string skill
    61     {
    62       get { return _skill; }
    63       set { _skill = value; }
    64     }
    65     private global::ProtoBuf.IExtension extensionObject;
    66     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
    67       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
    68   }
    69   
    70     [global::ProtoBuf.ProtoContract(Name=@"Sex")]
    71     public enum Sex
    72     {
    73             
    74       [global::ProtoBuf.ProtoEnum(Name=@"MALE", Value=0)]
    75       MALE = 0,
    76             
    77       [global::ProtoBuf.ProtoEnum(Name=@"FEMALE", Value=1)]
    78       FEMALE = 1
    79     }
    80   
    81 }
    View Code

    生成的test2.cs:

     1 //------------------------------------------------------------------------------
     2 // <auto-generated>
     3 //     This code was generated by a tool.
     4 //
     5 //     Changes to this file may cause incorrect behavior and will be lost if
     6 //     the code is regenerated.
     7 // </auto-generated>
     8 //------------------------------------------------------------------------------
     9 
    10 // Generated from: test2.proto
    11 // Note: requires additional types generated from: test1.proto
    12 namespace Test.App
    13 {
    14   [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SuperHero")]
    15   public partial class SuperHero : global::ProtoBuf.IExtensible
    16   {
    17     public SuperHero() {}
    18     
    19     private Test.Base.Hero _hero;
    20     [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"hero", DataFormat = global::ProtoBuf.DataFormat.Default)]
    21     public Test.Base.Hero hero
    22     {
    23       get { return _hero; }
    24       set { _hero = value; }
    25     }
    26     private string _superSkill;
    27     [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"superSkill", DataFormat = global::ProtoBuf.DataFormat.Default)]
    28     public string superSkill
    29     {
    30       get { return _superSkill; }
    31       set { _superSkill = value; }
    32     }
    33     private global::ProtoBuf.IExtension extensionObject;
    34     global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
    35       { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
    36   }
    37   
    38 }
    View Code

    下面我们看看工程中该如何使用生成的代码文件,先在VS中添加protobuf-net.dll的引用,文件在解压后的目录Full中,然后将生成的cs文件也包含到vs中。

    下面是将对象序列化为二进制文件和从二进制文件中反序列化出对象的代码:

     1 using ProtoBuf;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.IO;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 using Test.App;
     9 using Test.Base;
    10 
    11 namespace ProtobufTest
    12 {
    13     class Program
    14     {
    15         static void Main(string[] args)
    16         {
    17             ObjectToFile();
    18             FileToObject();
    19             Console.ReadKey();
    20         }
    21 
    22         private static void ObjectToFile()
    23         {
    24             //创建对象
    25             SuperHero sh = new SuperHero();
    26             sh.hero = new Hero();
    27             sh.superSkill = "天狼巽闪";
    28 
    29             sh.hero.people = new People();
    30             sh.hero.skill = "疾鹰七痕剑";
    31 
    32             sh.hero.people.name = "赛特";
    33             sh.hero.people.sex = Sex.MALE;
    34             sh.hero.people.age = 30;
    35 
    36             //序列化
    37             using(MemoryStream stream = new MemoryStream())
    38             {
    39                 //获取二进制数据
    40                 Serializer.Serialize<SuperHero>(stream, sh);
    41                 byte[] bytes = stream.ToArray();
    42                 stream.Close();
    43 
    44                 //写入数据
    45                 using(FileStream fs = File.Open("D:\test.bytes", FileMode.OpenOrCreate))
    46                 {
    47                     fs.Write(bytes, 0, bytes.Length);
    48                     fs.Flush();
    49                     fs.Close();
    50                 }
    51             }
    52 
    53             Console.WriteLine("文件已经生成!");
    54         }
    55 
    56         private static void FileToObject()
    57         {
    58             //读取数据
    59             using(FileStream fs = File.Open("D:\test.bytes", FileMode.Open))
    60             {
    61                 byte[] bytes = new byte[fs.Length];
    62                 fs.Read(bytes, 0, (int)fs.Length);
    63                 fs.Close();
    64 
    65                 //反序列化
    66                 using(MemoryStream stream = new MemoryStream(bytes))
    67                 {
    68                     SuperHero sh = Serializer.Deserialize<SuperHero>(stream);
    69                     stream.Close();
    70 
    71                     Console.WriteLine("姓名:{0},性别:{1},年龄:{2},绝技:{3},终极绝技:{4}", sh.hero.people.name, sh.hero.people.sex, sh.hero.people.age, sh.hero.skill, sh.superSkill);
    72                 }
    73             }
    74         }
    75     }
    76 }

    程序输出:

    1 文件已经生成!
    2 姓名:赛特,性别:MALE,年龄:30,绝技:疾鹰七痕剑,终极绝技:天狼巽闪
  • 相关阅读:
    Javascript 时间Date()的一般用法
    vue搭建
    初次见面
    css3边框
    appium移动自动化测试-one demo
    appium移动自动化测试-安装2
    appium移动自动化测试-安装1
    python数据类型
    判断软键盘是不是可见
    android 设置Dialog的宽度
  • 原文地址:https://www.cnblogs.com/hammerc/p/4473515.html
Copyright © 2020-2023  润新知