C# 网络协议第三方库Protobuf的使用

为什么要使用二进制数据

通常我们写一个简单的网络通讯软件可能使用的最多的是字符串类型,比较简单,例如发送格式为(head)19|Msg:Heart|100,x,y,z…,在接收端会解析收到的socket数据。
这样通常是完全可行的,但是随着数据量变大,网络吞吐量就变大,可能发送的字符串就不合适了,可能会数据量变大。
举例:
假如你要发送你的年收入和你的坐标,例如你的年收入是一亿两千万(123,456,789)(幸福死了)你的坐标是1.234567,如果通过字符串传输,你的收入就是9位,你的坐标可能你发小数因为精度问题还不准确
通常使用二进制发送会大大节省。一个int32是4位,float类型也是4位,这样8位就够了。
看下面的例子:

        static void Main(string[] args){Console.WriteLine("Hello, World!");int money = 123456789;float x = 1.234567f;byte[] moneybyte = BitConverter.GetBytes(money);byte[] xbyte = BitConverter.GetBytes(x);Console.WriteLine($"moneybyte: {moneybyte.Length},{money} :xbyte: {xbyte.Length},{x}" );int moneyget = BitConverter.ToInt32(moneybyte);float xget = BitConverter.ToSingle(xbyte);Console.WriteLine($"moneyget: {moneyget} :xget: {xget}");}

输出结果

Hello, World!
moneybyte: 4,123456789 :xbyte: 4,1.234567
moneyget: 123456789 :xget: 1.234567

我们看到对于数字32位占4个字节,这样如果是大量的数据就会很节省,甚至你可以使用int16,或者bool占用更小的字节。
对于大量密集的网络程序使用二进制数据进行发送很必要的。

初步思考如何方便的使用二进制或者封装

是不是有这样的疑问,如果要同步一个数据包含很多类型数据,如何拼接和解析呢,好像二进制没有字符串那么直观和好使用。
比如我要同步的数据是如下数据(通常我们把这种格式称作协议),需要发送结构和解析正确的匹配才能解析。
协议头|发送的大小|我的名字|18|123456789|1.234567|我的介绍|结束
对于二进制如果我们有这样的结构

public struct mydata
{public string name;public int age;public int money;public float x;public string readme;}

我们可以根据结构体内的属性进行二进制发送就可以了,接收方也有这样的数据结构也进行解析就可以了,这里要注意每个属性的顺序不能是错误的。
网上有一些把结构体或者类打包成二进制的方法,这里就不过多说明了。

使用Protobuf

protobuf就是专门为实现这个而生的,从名字就可以看出来。
Protobuf 的官方 C# 库是 Google.Protobuf,可以通过 NuGet 包管理器来方便的使用。
我们这里就来简单说一下如何使用:

安装

首先vs里创建一个c#控制台程序。
然后可以通过 NuGet安装
在这里插入图片描述

第一个协议

我们创建一个Person.proto文件

syntax = "proto3";message Person {string name = 1;int32 age = 2;string email = 3;
}

我们需要把这个proto转成c#可以解析的c#程序
我们可以来到Protobuf库下载执行程序,这个程序可以把proto解析成c#文件。

我们下载好之后:输入指令

F:\Downloads\protoc-29.3-win64\bin>protoc --csharp_out=. Person.protoF:\Downloads\protoc-29.3-win64\bin>

具体指令可以参考库里的文档
–csharp_out输出cs文件 .是当前路径
执行成功后会有一个Person.cs我们可以放入我们的项目,这样就很容易解析协议了。
生成的cs如下:

// <auto-generated>
//     Generated by the protocol buffer compiler.  DO NOT EDIT!
//     source: test.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021, 8981
#region Designer generated codeusing pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
/// <summary>Holder for reflection information generated from test.proto</summary>
public static partial class TestReflection {#region Descriptor/// <summary>File descriptor for test.proto</summary>public static pbr::FileDescriptor Descriptor {get { return descriptor; }}private static pbr::FileDescriptor descriptor;static TestReflection() {byte[] descriptorData = global::System.Convert.FromBase64String(string.Concat("Cgp0ZXN0LnByb3RvIjIKBlBlcnNvbhIMCgRuYW1lGAEgASgJEgsKA2FnZRgC","IAEoBRINCgVlbWFpbBgDIAEoCWIGcHJvdG8z"));descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,new pbr::FileDescriptor[] { },new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {new pbr::GeneratedClrTypeInfo(typeof(global::Person), global::Person.Parser, new[]{ "Name", "Age", "Email" }, null, null, null, null)}));}#endregion}
#region Messages
[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
public sealed partial class Person : pb::IMessage<Person>
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE, pb::IBufferMessage
#endif
{private static readonly pb::MessageParser<Person> _parser = new pb::MessageParser<Person>(() => new Person());private pb::UnknownFieldSet _unknownFields;[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public static pb::MessageParser<Person> Parser { get { return _parser; } }[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public static pbr::MessageDescriptor Descriptor {get { return global::TestReflection.Descriptor.MessageTypes[0]; }}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]pbr::MessageDescriptor pb::IMessage.Descriptor {get { return Descriptor; }}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person() {OnConstruction();}partial void OnConstruction();[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person(Person other) : this() {name_ = other.name_;age_ = other.age_;email_ = other.email_;_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public Person Clone() {return new Person(this);}/// <summary>Field number for the "name" field.</summary>public const int NameFieldNumber = 1;private string name_ = "";[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public string Name {get { return name_; }set {name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");}}/// <summary>Field number for the "age" field.</summary>public const int AgeFieldNumber = 2;private int age_;[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public int Age {get { return age_; }set {age_ = value;}}/// <summary>Field number for the "email" field.</summary>public const int EmailFieldNumber = 3;private string email_ = "";[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public string Email {get { return email_; }set {email_ = pb::ProtoPreconditions.CheckNotNull(value, "value");}}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override bool Equals(object other) {return Equals(other as Person);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public bool Equals(Person other) {if (ReferenceEquals(other, null)) {return false;}if (ReferenceEquals(other, this)) {return true;}if (Name != other.Name) return false;if (Age != other.Age) return false;if (Email != other.Email) return false;return Equals(_unknownFields, other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override int GetHashCode() {int hash = 1;if (Name.Length != 0) hash ^= Name.GetHashCode();if (Age != 0) hash ^= Age.GetHashCode();if (Email.Length != 0) hash ^= Email.GetHashCode();if (_unknownFields != null) {hash ^= _unknownFields.GetHashCode();}return hash;}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public override string ToString() {return pb::JsonFormatter.ToDiagnosticString(this);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void WriteTo(pb::CodedOutputStream output) {#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODEoutput.WriteRawMessage(this);#elseif (Name.Length != 0) {output.WriteRawTag(10);output.WriteString(Name);}if (Age != 0) {output.WriteRawTag(16);output.WriteInt32(Age);}if (Email.Length != 0) {output.WriteRawTag(26);output.WriteString(Email);}if (_unknownFields != null) {_unknownFields.WriteTo(output);}#endif}#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {if (Name.Length != 0) {output.WriteRawTag(10);output.WriteString(Name);}if (Age != 0) {output.WriteRawTag(16);output.WriteInt32(Age);}if (Email.Length != 0) {output.WriteRawTag(26);output.WriteString(Email);}if (_unknownFields != null) {_unknownFields.WriteTo(ref output);}}#endif[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public int CalculateSize() {int size = 0;if (Name.Length != 0) {size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);}if (Age != 0) {size += 1 + pb::CodedOutputStream.ComputeInt32Size(Age);}if (Email.Length != 0) {size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);}if (_unknownFields != null) {size += _unknownFields.CalculateSize();}return size;}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void MergeFrom(Person other) {if (other == null) {return;}if (other.Name.Length != 0) {Name = other.Name;}if (other.Age != 0) {Age = other.Age;}if (other.Email.Length != 0) {Email = other.Email;}_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);}[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]public void MergeFrom(pb::CodedInputStream input) {#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODEinput.ReadRawMessage(this);#elseuint tag;while ((tag = input.ReadTag()) != 0) {if ((tag & 7) == 4) {// Abort on any end group tag.return;}switch(tag) {default:_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);break;case 10: {Name = input.ReadString();break;}case 16: {Age = input.ReadInt32();break;}case 26: {Email = input.ReadString();break;}}}#endif}#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE[global::System.Diagnostics.DebuggerNonUserCodeAttribute][global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {uint tag;while ((tag = input.ReadTag()) != 0) {if ((tag & 7) == 4) {// Abort on any end group tag.return;}switch(tag) {default:_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);break;case 10: {Name = input.ReadString();break;}case 16: {Age = input.ReadInt32();break;}case 26: {Email = input.ReadString();break;}}}}#endif}#endregion#endregion Designer generated code

使用

我们开始使用

static void Main(string[] args)
{Console.WriteLine("Hello, World!");var person = new Person{Age = 18,Name = "",Email = ""};byte[] serializedData = person.ToByteArray();Console.WriteLine($"serialsize {serializedData.Length} :Serialized Data: "  + BitConverter.ToString(serializedData));// 从字节数组反序列化var deserializedPerson = Person.Parser.ParseFrom(serializedData);Console.WriteLine($"Deserialized Person: Name={deserializedPerson.Name}, Age={deserializedPerson.Age}, Email={deserializedPerson.Email}");}
代码中我们给person赋值,并通过ToByteArray二进制转化,得到二进制数组后就可以通过网络发送了。
当接收方收到这个二进制数据就可以通过ParseFrom进行解析。

执行结果

Hello, World!
serialsize 2 :Serialized Data: 10-12
Deserialized Person: Name=, Age=18, Email=

我们看到二进制大小是2是因为使用了一种变长编码 (varint) 的优化方案可以看下官方的文档。通常短数据比较多,使用变长编码的方式能够节省一些。

到这里就结束了。以上就是Protobuf的简单使用。

参考
https://github.com/protocolbuffers/protobuf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/5944.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软Win10 RP 19045.5435(KB5050081)预览版发布!

系统之家1月20日最新报道&#xff0c;微软面向Release Preview频道的Windows Insider项目成员&#xff0c;发布了适用于Windows10 22H2版本的KB5050081更新&#xff0c;更新后系统版本号将升至19045.5435。本次更新增加了对GB18030-2022标准的支持&#xff0c;同时新版日历将为…

零售业革命:改变行业的顶级物联网用例

mpro5 产品负责人Ruby Whipp表示&#xff0c;技术进步持续重塑零售业&#xff0c;其中物联网&#xff08;IoT&#xff09;正引领这一变革潮流。 研究表明&#xff0c;零售商们正在采用物联网解决方案&#xff0c;以提升运营效率并改善顾客体验。这些技术能够监控运营的各个方面…

ASP .NET Core 学习(.NET9)部署(一)windows

在windows部署 ASP .NET Core 的时候IIS是不二选择 一、IIS安装 不论是在window7 、w10还是Windows Server&#xff0c;都是十分简单的&#xff0c;下面以Windows10为例 打开控制面版—程序—启用或关闭Windows功能 勾选图中的两项&#xff0c;其中的子项看需求自行勾选&am…

【组件库】使用Vue2+AntV X6+ElementUI 实现拖拽配置自定义vue节点

先来看看实现效果&#xff1a; 【组件库】使用 AntV X6 ElementUI 实现拖拽配置自定义 Vue 节点 在现代前端开发中&#xff0c;流程图和可视化编辑器的需求日益增加。AntV X6 是一个强大的图形化框架&#xff0c;支持丰富的图形操作和自定义功能。结合 ElementUI&#xff0c;…

docker 部署confluence

1.安装docker的过程就不说了。 2.下载镜像。 docker pull cptactionhank/atlassian-confluence:7.4.0 docker images 3.下载pojie 包。 https://download.csdn.net/download/liudongyang123/90285042https://download.csdn.net/download/liudongyang123/90285042https://do…

C++ 二叉搜索树

目录 概念 性能分析 二叉搜索树的插入 二叉树的查找 二叉树的前序遍历 二叉搜索树的删除&#xff08;重点&#xff09; 完整代码 key与value的使用 概念 对于一个二叉搜索树 若它的左子树不为空&#xff0c;则左子树上所有的节点的值都小于等于根节点的值若它的右子树不为空…

重生之我在异世界学编程之C语言:深入指针篇(上)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文&#xff08;1&#xff09;内置数…

HTTP 配置与应用(不同网段)

想做一个自己学习的有关的csdn账号&#xff0c;努力奋斗......会更新我计算机网络实验课程的所有内容&#xff0c;还有其他的学习知识^_^&#xff0c;为自己巩固一下所学知识&#xff0c;下次更新校园网设计。 我是一个萌新小白&#xff0c;有误地方请大家指正&#xff0c;谢谢…

YOLOv5训练自己的数据及rknn部署

YOLOv5训练自己的数据及rknn部署 一、下载源码二、准备自己的数据集2.1 标注图像2.2 数据集结构 三、配置YOLOv5训练3.1 修改配置文件3.2 模型选择 四、训练五、测试六、部署6.1 pt转onnx6.2 onnx转rknn 七、常见错误7.1 训练过程中的错误7.1.1 cuda: out of memory7.1.2 train…

STM32 ST7735 128*160

ST7735 接口和 STM32 SPI 引脚连接 ST7735 引脚功能描述STM32 引脚连接&#xff08;示例&#xff0c;使用 SPI1&#xff09;SCLSPI 时钟信号 (SCK)PA0(SPI1_SCK)SDASPI 数据信号 (MOSI)PA1 (SPI1_MOSI)RST复位信号 (Reset)PA2(GPIO 手动控制)DC数据/命令选择 (D/C)PA3 (GPIO 手…

使用Redis缓解数据库压力+三种常见问题

目录 一.如何使用 Redis 缓存减缓数据库的压力 &#xff1a; 二.Redis缓存穿透&#xff1a; 布隆过滤器的工作原理&#xff1a; 三.缓存雪崩&#xff1a; 缓存雪崩的处理策略&#xff1a; 四.缓存击穿&#xff1a; 使用 Redis 缓存来减缓数据库的压力是一个常见的优化手段…

移动端VR处理器和传统显卡的不同

骁龙 XR 系列芯片 更多地依赖 AI 技术 来优化渲染过程&#xff0c;而传统的 GPU 渲染 则倾向于在低画质下运行以减少负载。这种设计是为了在有限的硬件资源下&#xff08;如移动端 XR 设备&#xff09;实现高性能和低功耗的平衡。以下是具体的分析&#xff1a; 1. AI 驱动的渲染…

AI新玩法:Flux.1图像生成结合内网穿透远程生图的解决方案

文章目录 前言1. 本地部署ComfyUI2. 下载 Flux.1 模型3. 下载CLIP模型4. 下载 VAE 模型5. 演示文生图6. 公网使用 Flux.1 大模型6.1 创建远程连接公网地址 7. 固定远程访问公网地址 前言 在这个AI技术日新月异的时代&#xff0c;图像生成模型已经成为了创意工作者和开发者手中…

WordPress果果对象存储插件

将网站上的图片等静态资源文件上传至七牛云对象存储&#xff0c;可以减轻服务器文件存储压力&#xff0c;提升静态文件访问速度&#xff0c;从而加速网站访问速度。 支持&#xff1a;阿里云对象存储、华为云对象存储、百度云对象存储、腾讯云对象存储、七牛云对象存储。 下载…

2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总

数学建模美赛倒计时&#xff0c;对于第一次参加竞赛且没有相关基础知识的同学来讲&#xff0c;掌握数学建模常用经典的模型算法知识&#xff0c;并熟练使用相关软件进行建模是关键。本文将介绍一些常用的模型算法&#xff0c;以及软件操作教程。 数学建模常用模型包括&#xf…

Maven的下载安装配置

maven的下载安装配置 maven是什么 Maven 是一个用于 Java 平台的 自动化构建工具&#xff0c;由 Apache 组织提供。它不仅可以用作包管理&#xff0c;还支持项目的开发、打包、测试及部署等一系列行为 Maven的核心功能 项目构建生命周期管理&#xff1a;Maven定义了项目构建…

2000-2010年各省第三产业就业人数数据

2000-2010年各省第三产业就业人数数据 1、时间&#xff1a;2000-2010年 2、来源&#xff1a;统计年鉴、各省年鉴 3、指标&#xff1a;行政区划代码、地区、年份、第三产业就业人员数&#xff08;万人&#xff09; 4、范围&#xff1a;31省 5、指标解释&#xff1a;第三产业…

【系统环境丢失恢复】如何恢复和重建 Ubuntu 中的 .bashrc 文件

r如果你遇到这种情况&#xff0c;说明系统环境的.bashrc 文件丢失恢复&#xff1a; 要恢复 ~/.bashrc 文件&#xff0c;可以按照以下几种方式操作&#xff1a; 恢复默认的 ~/.bashrc 文件 如果 ~/.bashrc 文件被删除或修改&#xff0c;你可以恢复到默认的版本。可以参考以下…

Docker网段和服务器ip冲突导致无法访问网络的解决方法

若宿主机所在网络的网段为172.[17-31].xx.xx&#xff0c;则会与Docker本身内部网络间出现冲突&#xff0c;此时需要重新配置Docker默认地址池 一&#xff1a;查看docker的默认网段 route 二&#xff1a;修改docker的默认网段 etc/docker/daemon.json文件增加修改网段信息 {…

2.2.1 语句结构

ST&#xff08;Structured Text&#xff09;语言是一种基于IEC 61131-3标准的高级文本编程语言&#xff0c;其语法规则严格且清晰。以下是ST语言中关于分号、注释和代码块的详细语法规则说明&#xff1a; 分号&#xff08;;&#xff09; 作用&#xff1a;分号用于表示语句的结…