对于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 }