首页 技术 正文
技术 2022年11月15日
0 收藏 353 点赞 2,951 浏览 9442 个字

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

 //这里定义基础的消息体或枚举 package Test.Base; //定义枚举 enum Sex
{
MALE = ;
FEMALE = ;
} //定义消息体 message People
{
required string name = ;
required Sex sex = ;
optional int32 age = ;
} message Hero
{
required People people = ;
optional string skill = ;
}

test2.proto:

 //有用到 test1 的东西需要导入 test1 import "test1.proto"; //这里定义直接使用的消息体 package Test.App; //定义消息体 message SuperHero
{
required Test.Base.Hero hero = ;
required string superSkill = ;
}

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

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

 D:\project\tool\protobuf-net\ProtoGen\protogen.exe -i:test1.proto -i:test2.proto -o:test.cs
pause

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

生成的test.cs内容如下:

 //------------------------------------------------------------------------------
// <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: test1.proto
namespace Test.Base
{
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"People")]
public partial class People : global::ProtoBuf.IExtensible
{
public People() {} private string _name;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"name", DataFormat = global::ProtoBuf.DataFormat.Default)]
public string name
{
get { return _name; }
set { _name = value; }
}
private Test.Base.Sex _sex;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"sex", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
public Test.Base.Sex sex
{
get { return _sex; }
set { _sex = value; }
}
private int _age = default(int);
[global::ProtoBuf.ProtoMember(, IsRequired = false, Name=@"age", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
[global::System.ComponentModel.DefaultValue(default(int))]
public int age
{
get { return _age; }
set { _age = value; }
}
private global::ProtoBuf.IExtension extensionObject;
global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
{ return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
} [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Hero")]
public partial class Hero : global::ProtoBuf.IExtensible
{
public Hero() {} private Test.Base.People _people;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"people", DataFormat = global::ProtoBuf.DataFormat.Default)]
public Test.Base.People people
{
get { return _people; }
set { _people = value; }
}
private string _skill = "";
[global::ProtoBuf.ProtoMember(, IsRequired = false, Name=@"skill", DataFormat = global::ProtoBuf.DataFormat.Default)]
[global::System.ComponentModel.DefaultValue("")]
public string skill
{
get { return _skill; }
set { _skill = 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=@"Sex")]
public enum Sex
{ [global::ProtoBuf.ProtoEnum(Name=@"MALE", Value=)]
MALE = , [global::ProtoBuf.ProtoEnum(Name=@"FEMALE", Value=)]
FEMALE =
} }
// Generated from: test2.proto
// Note: requires additional types generated from: test1.proto
namespace Test.App
{
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SuperHero")]
public partial class SuperHero : global::ProtoBuf.IExtensible
{
public SuperHero() {} private Test.Base.Hero _hero;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"hero", DataFormat = global::ProtoBuf.DataFormat.Default)]
public Test.Base.Hero hero
{
get { return _hero; }
set { _hero = value; }
}
private string _superSkill;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"superSkill", DataFormat = global::ProtoBuf.DataFormat.Default)]
public string superSkill
{
get { return _superSkill; }
set { _superSkill = value; }
}
private global::ProtoBuf.IExtension extensionObject;
global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
{ return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
} }

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

 D:\project\tool\protobuf-net\ProtoGen\protogen.exe -i:test1.proto -o:test1.cs
D:\project\tool\protobuf-net\ProtoGen\protogen.exe -i:test2.proto -o:test2.cs
pause

生成的test1.cs:

 //------------------------------------------------------------------------------
// <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: test1.proto
namespace Test.Base
{
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"People")]
public partial class People : global::ProtoBuf.IExtensible
{
public People() {} private string _name;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"name", DataFormat = global::ProtoBuf.DataFormat.Default)]
public string name
{
get { return _name; }
set { _name = value; }
}
private Test.Base.Sex _sex;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"sex", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
public Test.Base.Sex sex
{
get { return _sex; }
set { _sex = value; }
}
private int _age = default(int);
[global::ProtoBuf.ProtoMember(, IsRequired = false, Name=@"age", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
[global::System.ComponentModel.DefaultValue(default(int))]
public int age
{
get { return _age; }
set { _age = value; }
}
private global::ProtoBuf.IExtension extensionObject;
global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
{ return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
} [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"Hero")]
public partial class Hero : global::ProtoBuf.IExtensible
{
public Hero() {} private Test.Base.People _people;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"people", DataFormat = global::ProtoBuf.DataFormat.Default)]
public Test.Base.People people
{
get { return _people; }
set { _people = value; }
}
private string _skill = "";
[global::ProtoBuf.ProtoMember(, IsRequired = false, Name=@"skill", DataFormat = global::ProtoBuf.DataFormat.Default)]
[global::System.ComponentModel.DefaultValue("")]
public string skill
{
get { return _skill; }
set { _skill = 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=@"Sex")]
public enum Sex
{ [global::ProtoBuf.ProtoEnum(Name=@"MALE", Value=)]
MALE = , [global::ProtoBuf.ProtoEnum(Name=@"FEMALE", Value=)]
FEMALE =
} }

生成的test2.cs:

 //------------------------------------------------------------------------------
// <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: test2.proto
// Note: requires additional types generated from: test1.proto
namespace Test.App
{
[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"SuperHero")]
public partial class SuperHero : global::ProtoBuf.IExtensible
{
public SuperHero() {} private Test.Base.Hero _hero;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"hero", DataFormat = global::ProtoBuf.DataFormat.Default)]
public Test.Base.Hero hero
{
get { return _hero; }
set { _hero = value; }
}
private string _superSkill;
[global::ProtoBuf.ProtoMember(, IsRequired = true, Name=@"superSkill", DataFormat = global::ProtoBuf.DataFormat.Default)]
public string superSkill
{
get { return _superSkill; }
set { _superSkill = value; }
}
private global::ProtoBuf.IExtension extensionObject;
global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
{ return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
} }

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

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

 using ProtoBuf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Test.App;
using Test.Base; namespace ProtobufTest
{
class Program
{
static void Main(string[] args)
{
ObjectToFile();
FileToObject();
Console.ReadKey();
} private static void ObjectToFile()
{
//创建对象
SuperHero sh = new SuperHero();
sh.hero = new Hero();
sh.superSkill = "天狼巽闪"; sh.hero.people = new People();
sh.hero.skill = "疾鹰七痕剑"; sh.hero.people.name = "赛特";
sh.hero.people.sex = Sex.MALE;
sh.hero.people.age = ; //序列化
using(MemoryStream stream = new MemoryStream())
{
//获取二进制数据
Serializer.Serialize<SuperHero>(stream, sh);
byte[] bytes = stream.ToArray();
stream.Close(); //写入数据
using(FileStream fs = File.Open("D:\\test.bytes", FileMode.OpenOrCreate))
{
fs.Write(bytes, , bytes.Length);
fs.Flush();
fs.Close();
}
} Console.WriteLine("文件已经生成!");
} private static void FileToObject()
{
//读取数据
using(FileStream fs = File.Open("D:\\test.bytes", FileMode.Open))
{
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, , (int)fs.Length);
fs.Close(); //反序列化
using(MemoryStream stream = new MemoryStream(bytes))
{
SuperHero sh = Serializer.Deserialize<SuperHero>(stream);
stream.Close(); Console.WriteLine("姓名:{0},性别:{1},年龄:{2},绝技:{3},终极绝技:{4}", sh.hero.people.name, sh.hero.people.sex, sh.hero.people.age, sh.hero.skill, sh.superSkill);
}
}
}
}
}

程序输出:

 文件已经生成!
姓名:赛特,性别:MALE,年龄:,绝技:疾鹰七痕剑,终极绝技:天狼巽闪
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,999
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,511
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,357
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,140
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,770
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,848