在.NET中使用Protobuf框架

 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,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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脚本的简单例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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脚本生成的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//------------------------------------------------------------------------------
// <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); }
  }
   
}

  

*********************分割线*********************

 

最近又玩了一下pbnet,发现pbnet的github工程里面提供了各个版本的protobuf-net的版本,而且需要自己来编译:
     
    protobuf-net的实现没有依赖任何.net 4.0的特性,所以net30版本就能提供完整的功能
    net30和net20版本唯一的区别:net30支持wcf
    每个版本里面包含三个文件:
        protobuf-net.dll:库文件,需要放到应用工程里面的
        protobuf-net.xml:IDE使用的配置
        protobuf-net.pdb:deug时使用的symblos,用来定位崩溃之类的问题 
 
版本
说明
net20 regular .NET 2.0 (excludes WCF hooks)
net30 regular .NET 3.0 or above (including 3.5, 4.0, 4.5, ...)
netcore45 windows store apps / windows runtime 4.5
sl4 silverlight 4 or above 
代码生成工具还是和以前一样:
    shell sample:
    目录下的每个proto生成对应的cs文件:
    FOR /R %%G IN (*.proto) DO ..\ProtoBuf\protogen.exe -i:%%G -o:%%~nG_pb.cs 

 

    其中ns用来设置namespace,只有在proto里面没有指定namespace才会生效。 
 
不过,proto3.0目前已知支持c#了,所以就可以直接使用官方版本了:
  (1)使用源码来编译生成Google.Protobuf.dll;
  (2)使用protoc3分析proto生成对应的c#代码。
  protoc3生成c#代码的语法,和之前pbnet略有不同:
    protoc3 -I=ProtoDir --csharp_out=CSDir ProtoFileName.proto
  说明:
        ProtoDir为proto定义的目录;
        CSDir为生成的对应的***.cs的文件目录;
        ProtoFileName.proto就是要生成的proto文件名 。
posted @   Tammytan  阅读(892)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示