c# 中针对Protobuf的应用
Protobuf介绍
Protobuf是Protocol buffers的简称,是由Google公司推出的一种数据交换格式,与传统的xml、json有着相同的作用,相对于xml和Json的区别是,Protobuf省略了数据的自描述所以数据更小,采用的是二进制的数据格式所以有很高的转换效率。同时也支持多种编程语言兼容。是用于数据传递,数据存储的一种较好方式。
Protobuf语法
protobuf协议文件是以.proto结尾的文本文件。
syntax = "proto3";
package ProtoBuffTest;
message DataItem {
string ID = 1;
repeated string IDArray = 2;
}
message DataStruct {
string ID = 1;
string Name = 2;
repeated int32 Numbers = 3 [packed = false];
repeated uint32 Buffer = 4 [packed = false];
DataItem DataItem = 5;
repeated DataItem DataItems = 6;
repeated int32 Ints = 7 [packed = false];
bytes Bytes = 8;
DataType dataType = 9;
}
enum DataType {
Byte = 0;
Short = 1;
}
标识符
- syntax:标识使用的protobuf是哪个版本。上面表示使用的是3.x版本。
- package:标识生成目标文件的包名。在C#、C++中表示的是命名空间。java 等语言中表示的是包名
- enum:表示一个枚举类型。会在目标.h文件中自动生成一个枚举类型。
- message:标识一条消息。会在目标文件中自动生成一个类。
字段
- 字段格式:
role type name = tag [default value]
- role 有三种取值:
-
required:该字段必须给值,不能为空。否则message被认为是未初始化的。如果试图建立一个未初始化的message将会抛出RuntimeException异常,解析未初始化的message会抛出IOException异常。
-
optional:表示该字段是可选值,可以为空。如果不设置,会设置一个默认值。也可以自定义默认值。如果没有自定义默认值,会是用系统默认值。
-
repeated:表示该字段可以重复,可等同于动态数组。
注意:required字段是永久性的,如果之后不使用该字段,或者该字段标识改为optional或repeated,那么使用就接口读取新协议时,如果发现没有该字段,会认为该消息不完整,会拒收或者丢弃该消息。
字段类型
Protobuf数据类型 | 描述 |
---|---|
bool | 布尔类型 |
double | 64位浮点数 |
float | 32为浮点数 |
int32 | 32位整数、 |
uin32 | 无符号32位整数 |
int64 | 64位整数 |
uint64 | 64为无符号整 |
sint32 | 32位整数,处理负数率更高 |
fixed32 | 64位整数处理负数效率更高 |
fixed32 | 32位无符号整数 |
fixed64 | 64位无符号整数 |
sfixed32 | 32位整数、能以更高的效率处理负数 |
sfixed64 | 64为整数 |
string | 只能处理ASCII字符 |
bytes | 用于处理多字节的语言字符、如中文 |
enum | 可以包含一个用户自定义的枚举类型uint32 |
message | 可以包含一个用户自定义的消息类型 |
C#中针对Protobuf的使用
在C#中可以使用March Gravel提供的C#库Protobuf-net来完成对Protobuf数据结构的应用。
基本用法
- 定义Protobuf数据结构
[ProtoContract]
public class DataStruct
{
[ProtoMember(1)]
public string ID { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public List<int> Numbers { get; set; }
[ProtoMember(4)]
public List<byte> Buffer { get; set; }
[ProtoMember(5)]
public DataItem DataItem { get; set; }
[ProtoMember(6)]
public List<DataItem> DataItems { get; set; }
[ProtoMember(7)]
public int[] Ints { get; set; }
[ProtoMember(8)]
public byte[] Bytes { get; set; }
[ProtoMember(9)]
public DataType dataType { get; set; }
}
[ProtoContract]
public enum DataType
{
[ProtoMember(1)]
Byte = 0,
[ProtoMember(2)]
Short = 1
}
[ProtoContract]
public class DataItem
{
[ProtoMember(1)]
public string ID { get; set; } = "";
[ProtoMember(2)]
public List<string> IDArray { get; set; } = new List<string>();
}
- 导出Proto代码
SchemaGenerationOptions options =new SchemaGenerationOptions();
options.Types.Add(typeof(DataStruct));
options.Types.Add(typeof(DataItem));
options.Types.Add(typeof(DataType));
var proto = Serializer.GetProto(options);
Console.WriteLine(proto);
- 序列化数据
public static byte[] ToBytes(object obj)
{
try{
using(MemoryStream ms=new MemoryStream )
{
Serializer.Serialize(ms,obj);
var buffer=ms.GetBuffer();
var dataBuffer=new byte[ms.Length];
Array.Copy(buffer,dataBuffer,ms.Length);
ms.Dispose();
return dataBuffer;
}
}catch{
return null;
}
}
- 反序列化数据
public static T ToObj<T>(byte[] bufferData)
{
if (bufferData == null)
{
return default;
}
try{
using( MemoryStream ms= new MemoryStream ())
{
ms.SetLength(bufferData.Length);
ms.Write(bufferData, 0, bufferData.Length);
ms.Position = 0L;memoryStream.Flush();
T result = Serializer.Deserialize<T>(memoryStream);
ms.Dispose();
return result;
}
}catch
{
return default;
}
}