Fork me on GitHub
protobuf-net-data

protobuf-net

http://www.codeproject.com/Articles/642677/Protobuf-net-the-unofficial-manual

https://github.com/rdingwall/protobuf-net-data

https://github.com/mgravell/protobuf-net

protobuf是google的一个开源项目,可用于以下两种用途:

  (1)数据的存储(序列化和反序列化),类似于xml、json等;

  (2)制作网络通信协议。

  源代码下载地址:https://github.com/mgravell/protobuf-net

  开源项目地址如下:https://code.google.com/p/protobuf-net/,下载解压后的目录如下所示,每个文件夹的详细介绍都在最后一个txt文件里面了。

  

  ProtoGen是用来根据***.proto文件生成对应的***.cs文件的,而做数据存储功能只需要用到protobuf-net.dll即可,至于使用哪个版本项目情况决定。下面的例子在Windows平台下新建一个C#的控制台工程,并引入ProtoBufNet\Full\net30\protobuf-net.dll,代码如下所示:

复制代码
namespace TestProtoBuf
{
    [ProtoContract]
    public class Address
    {
        [ProtoMember(1)]
        public string Line1;
        [ProtoMember(2)]
        public string Line2;
    }

    [ProtoContract]
    public class Person
    {
        [ProtoMember(1)]
        public int Id;
        [ProtoMember(2)]
        public string Name;
        [ProtoMember(3)]
        public Address Addr;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Id = 1;
            person.Name = "First";
            person.Addr = new Address { Line1="line1", Line2="line2"};

            // ProtoBuf序列化
            using(var file = System.IO.File.Create("Person.bin"))
            {
                ProtoBuf.Serializer.Serialize(file, person);
            }

            // ProtoBuf反序列化
            Person binPerson = null;
            using(var file = System.IO.File.OpenRead("Person.bin"))
            {
                binPerson = ProtoBuf.Serializer.Deserialize<Person>(file);
            }

            System.Console.WriteLine(binPerson.Name);
        }
    }
}
复制代码

  可以看到序列化和反序列化的代码非常简单。

  protobuf提供了一种proto脚本用来编写***.proto文件,这种脚本格式简单、可读性强、方便扩展,用proto脚本定义网络协议是非常好用的。

  下面是一个proto脚本的简单例子:

复制代码
message Person {
    required string name=1;
    required int32 id=2;
    optional string email=3;

    enum PhoneType {
        MOBILE=0;
        HOME=1;
        WORK=2;
    }

    message PhoneNumber {
        required string number=1;
        optional PhoneType type=2 [default=HOME];
    }

    repeated PhoneNumber phone=4;
}
复制代码

  requied是必须有的字段、optional是可有可无的字段、repeated是可以重复的字段(数组或列表),同时枚举字段都必须给出默认值。

  接下来就可以使用ProgoGen来根据proto脚本生成源代码cs文件了,命令行如下:

  protogen -i:test.proto -0:test.cs -ns:MyProtoBuf

  -i指定了输入,-o指定了输出,-ns指定了生成代码的namespace,上面的proto脚本生成的源码如下:

复制代码
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// Generated from: file/pb.proto
namespace MyProtoBuf
{
  [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Person")]
  public partial class Person : global::ProtoBuf.IExtensible
  {
    public Person() {}
    
    private string _name;
    [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"name", DataFormat = global::ProtoBuf.DataFormat.Default)]
    public string name
    {
      get { return _name; }
      set { _name = value; }
    }
    private int _id;
    [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"id", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
    public int id
    {
      get { return _id; }
      set { _id = value; }
    }
    private string _email = "";
    [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"email", DataFormat = global::ProtoBuf.DataFormat.Default)]
    [global::System.ComponentModel.DefaultValue("")]
    public string email
    {
      get { return _email; }
      set { _email = value; }
    }
    private readonly global::System.Collections.Generic.List<Person.PhoneNumber> _phone = new global::System.Collections.Generic.List<Person.PhoneNumber>();
    [global::ProtoBuf.ProtoMember(4, Name=@"phone", DataFormat = global::ProtoBuf.DataFormat.Default)]
    public global::System.Collections.Generic.List<Person.PhoneNumber> phone
    {
      get { return _phone; }
    }
  
  [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"PhoneNumber")]
  public partial class PhoneNumber : global::ProtoBuf.IExtensible
  {
    public PhoneNumber() {}
    
    private string _number;
    [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"number", DataFormat = global::ProtoBuf.DataFormat.Default)]
    public string number
    {
      get { return _number; }
      set { _number = value; }
    }
    private Person.PhoneType _type = Person.PhoneType.HOME;
    [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"type", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
    [global::System.ComponentModel.DefaultValue(Person.PhoneType.HOME)]
    public Person.PhoneType type
    {
      get { return _type; }
      set { _type = value; }
    }
    private global::ProtoBuf.IExtension extensionObject;
    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
      { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
  }
  
    [global::ProtoBuf.ProtoContract(Name=@"PhoneType")]
    public enum PhoneType
    {
            
      [global::ProtoBuf.ProtoEnum(Name=@"MOBILE", Value=0)]
      MOBILE = 0,
            
      [global::ProtoBuf.ProtoEnum(Name=@"HOME", Value=1)]
      HOME = 1,
            
      [global::ProtoBuf.ProtoEnum(Name=@"WORK", Value=2)]
      WORK = 2
    }
  
    private global::ProtoBuf.IExtension extensionObject;
    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
      { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
  }
  
}
复制代码

  to be continue...

 

什么是ProtoBuf-net

Protobuf是google开源的一个项目,用户数据序列化反序列化,google声称google的数据通信都是用该序列化方法。它比xml格式要少的多,甚至比二进制数据格式也小的多。

Protobuf格式协议和xml一样具有平台独立性,可以在不同平台间通信,通信所需资源很少,并可以扩展,可以旧的协议上添加新数据

Protobuf是在java和c++运行的,Protobuf-net当然就是Protobuf在.net环境下的移植。

请参见:https://code.google.com/p/protobuf-net/ 

Get Start

[ProtoBuf.ProtoContract]
    public class Person
    {
        [ProtoBuf.ProtoMember(1)]
        public int Id { get; set; }
        [ProtoBuf.ProtoMember(2)]
        public string Name { get; set; }
        [ProtoBuf.ProtoMember(3)]
        public Address Address { get; set; }
    }

    [ProtoBuf.ProtoContract]
    public class Address
    {
        [ProtoBuf.ProtoMember(1)]
        public string Line1 { get; set; }
        [ProtoBuf.ProtoMember(2)]
        public string Line2 { get; set; }
    }

类前加上ProtoContract Attrbuit,成员加上ProtoMember Attribute即可,其中ProtoMember需要一个大于0的int类型的值,原则上这个int类型没有大小限制,但建议从1开始,这是一个良好的习惯,另外这个参数必需是这个类成员的唯一标识,不可重复

序列化

            var person = new Person
            {
                Id = 1,
                Name = "First",
                Address = new Address { Line1 = "Line1", Line2 = "Line2" }
            };
            using (var file = System.IO.File.Create("Person.bin"))
            {
                ProtoBuf.Serializer.Serialize(file, person);
            }

 

反序列化

Person newPerson;
using (var file = System.IO.File.OpenRead("Person.bin"))
{
    newPerson = ProtoBuf.Serializer.Deserialize<Person>(file);
}
使用起来很简单,代码移植也会相当方便,下面我要对比下序列化的文件大小。
1.使用ProtoBuf序列化1000个对象,查看Person.bin文件大小为:30 KB (29,760 字节)
List<Person> list = new List<Person>();
            for (var i = 0; i < 1000; i++)
            {
                var person = new Person
                {
                    Id = i,
                    Name = "Name"+i,
                    Address = new Address { Line1 = "Line1", Line2 = "Line2" }
                };
                list.Add(person);
            }

            using (var file = System.IO.File.Create("Person.bin"))
            {
                ProtoBuf.Serializer.Serialize(file, list);
            }

2.使用xml序列化1000个对象,Person.xml大小为:152 KB (155,935 字节)

            List<Person> list = new List<Person>();
            for (var i = 0; i < 1000; i++)
            {
                var person = new Person
                {
                    Id = i,
                    Name = "Name"+i,
                    Address = new Address { Line1 = "Line1", Line2 = "Line2" }
                };
                list.Add(person);
            }

            System.Xml.Serialization.XmlSerializer xmlSerizlizer = new System.Xml.Serialization.XmlSerializer(typeof(List<Person>));
            using(var file= System.IO.File.Create("Persion.xml")){
                xmlSerizlizer.Serialize(file, list);
            }

3. 使用binary序列化1000个对象,Person.dat大小为:54.1 KB (55,445 字节)

            List<Person> list = new List<Person>();
            for (var i = 0; i < 1000; i++)
            {
                var person = new Person
                {
                    Id = i,
                    Name = "Name"+i,
                    Address = new Address { Line1 = "Line1", Line2 = "Line2" }
                };
                list.Add(person);
            }
            
            using(var file = new System.IO.FileStream("Person.dat", System.IO.FileMode.Create))
            {
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter =
                    new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                binaryFormatter.Serialize(file, list);
            }

image

在这个实验中ProtoBuf比xml序列化小5倍,比二进制也近小一倍,有人说ProtoBuf比xml可以小到20倍,根据数据的复杂度这是有可能的。ProtoBuf的数据格式做为数据报文有着绝对优势,当然也有个弊端,它是2进制报文,没有xml格式这样的可读性,要想看懂报文内容只能用ProtoBuf反序列化了,不过我认识这基本上不是问题~

分类: C#
posted on 2015-01-16 14:18  HackerVirus  阅读(664)  评论(0编辑  收藏  举报