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\tool\protobuf-net\ProtoGen\protogen.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\tool\protobuf-net\ProtoGen\protogen.exe -i:test1.proto -o:test1.cs
2 D:\project\tool\protobuf-net\ProtoGen\protogen.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,绝技:疾鹰七痕剑,终极绝技:天狼巽闪

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。