最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#網(wǎng)絡(luò)協(xié)議第三方庫(kù)Protobuf的使用詳解

 更新時(shí)間:2025年12月24日 10:47:36   作者:Thinbug  
文章介紹了使用二進(jìn)制數(shù)據(jù)傳輸?shù)谋匾?并詳細(xì)介紹了Protobuf(Protocol Buffers)的使用方法,通過(guò)將協(xié)議定義文件(.proto)轉(zhuǎn)換為C#代碼,可以方便地進(jìn)行二進(jìn)制數(shù)據(jù)的發(fā)送和解析

為什么要使用二進(jìn)制數(shù)據(jù)

通常我們寫一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)通訊軟件可能使用的最多的是字符串類型,比較簡(jiǎn)單,例如發(fā)送格式為(head)19|Msg:Heart|100,x,y,z…,在接收端會(huì)解析收到的socket數(shù)據(jù)。

這樣通常是完全可行的,但是隨著數(shù)據(jù)量變大,網(wǎng)絡(luò)吞吐量就變大,可能發(fā)送的字符串就不合適了,可能會(huì)數(shù)據(jù)量變大。

舉例:

假如你要發(fā)送你的年收入和你的坐標(biāo),例如你的年收入是一億兩千萬(wàn)(123,456,789)(幸福死了)你的坐標(biāo)是1.234567,如果通過(guò)字符串傳輸,你的收入就是9位,你的坐標(biāo)可能你發(fā)小數(shù)因?yàn)榫葐?wèn)題還不準(zhǔn)確通常使用二進(jìn)制發(fā)送會(huì)大大節(jié)省。一個(gè)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}");
		}

輸出結(jié)果

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

我們看到對(duì)于數(shù)字32位占4個(gè)字節(jié),這樣如果是大量的數(shù)據(jù)就會(huì)很節(jié)省,甚至你可以使用int16,或者bool占用更小的字節(jié)。

對(duì)于大量密集的網(wǎng)絡(luò)程序使用二進(jìn)制數(shù)據(jù)進(jìn)行發(fā)送很必要的。

初步思考如何方便的使用二進(jìn)制或者封裝

是不是有這樣的疑問(wèn),如果要同步一個(gè)數(shù)據(jù)包含很多類型數(shù)據(jù),如何拼接和解析呢,好像二進(jìn)制沒(méi)有字符串那么直觀和好使用。

比如我要同步的數(shù)據(jù)是如下數(shù)據(jù)(通常我們把這種格式稱作協(xié)議),需要發(fā)送結(jié)構(gòu)和解析正確的匹配才能解析。

協(xié)議頭|發(fā)送的大小|我的名字|18|123456789|1.234567|我的介紹|結(jié)束

對(duì)于二進(jìn)制如果我們有這樣的結(jié)構(gòu)

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

我們可以根據(jù)結(jié)構(gòu)體內(nèi)的屬性進(jìn)行二進(jìn)制發(fā)送就可以了,接收方也有這樣的數(shù)據(jù)結(jié)構(gòu)也進(jìn)行解析就可以了,這里要注意每個(gè)屬性的順序不能是錯(cuò)誤的。

網(wǎng)上有一些把結(jié)構(gòu)體或者類打包成二進(jìn)制的方法,這里就不過(guò)多說(shuō)明了。

使用Protobuf

protobuf就是專門為實(shí)現(xiàn)這個(gè)而生的,從名字就可以看出來(lái)。

Protobuf 的官方 C# 庫(kù)是 Google.Protobuf,可以通過(guò) NuGet 包管理器來(lái)方便的使用。

我們這里就來(lái)簡(jiǎn)單說(shuō)一下如何使用:

安裝

首先vs里創(chuàng)建一個(gè)c#控制臺(tái)程序。

然后可以通過(guò) NuGet安裝

第一個(gè)協(xié)議

我們創(chuàng)建一個(gè)Person.proto文件

syntax = "proto3";

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

我們需要把這個(gè)proto轉(zhuǎn)成c#可以解析的c#程序

我們可以來(lái)到Protobuf庫(kù)下載執(zhí)行程序,這個(gè)程序可以把proto解析成c#文件。

我們下載好之后:輸入指令

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

F:\Downloads\protoc-29.3-win64\bin>

具體指令可以參考庫(kù)里的文檔

–csharp_out輸出cs文件 .是當(dāng)前路徑

執(zhí)行成功后會(huì)有一個(gè)Person.cs我們可以放入我們的項(xiàng)目,這樣就很容易解析協(xié)議了。

生成的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 code

using 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_MODE
    output.WriteRawMessage(this);
  #else
    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(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_MODE
    input.ReadRawMessage(this);
  #else
    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, 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

使用

我們開(kāi)始使用

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));

    // 從字節(jié)數(shù)組反序列化
    var deserializedPerson = Person.Parser.ParseFrom(serializedData);
    Console.WriteLine($"Deserialized Person: Name={deserializedPerson.Name}, Age={deserializedPerson.Age}, Email={deserializedPerson.Email}");

}
代碼中我們給person賦值,并通過(guò)ToByteArray二進(jìn)制轉(zhuǎn)化,得到二進(jìn)制數(shù)組后就可以通過(guò)網(wǎng)絡(luò)發(fā)送了。
當(dāng)接收方收到這個(gè)二進(jìn)制數(shù)據(jù)就可以通過(guò)ParseFrom進(jìn)行解析。

執(zhí)行結(jié)果

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

我們看到二進(jìn)制大小是2是因?yàn)槭褂昧艘环N變長(zhǎng)編碼 (varint) 的優(yōu)化方案可以看下官方的文檔。通常短數(shù)據(jù)比較多,使用變長(zhǎng)編碼的方式能夠節(jié)省一些。

總結(jié)

到這里就結(jié)束了。以上就是Protobuf的簡(jiǎn)單使用。

這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C# 添加、修改以及刪除Excel迷你圖表的實(shí)現(xiàn)方法

    C# 添加、修改以及刪除Excel迷你圖表的實(shí)現(xiàn)方法

    下面小編就為大家分享一篇C# 添加、修改以及刪除Excel迷你圖表的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • C# 將透明圖片的非透明區(qū)域轉(zhuǎn)換成Region的實(shí)例代碼

    C# 將透明圖片的非透明區(qū)域轉(zhuǎn)換成Region的實(shí)例代碼

    以下代碼實(shí)現(xiàn)將一張帶透明度的png圖片的非透明部分轉(zhuǎn)換成Region輸出的方法,有需要的朋友可以參考一下
    2013-10-10
  • 如何利用C#正則表達(dá)式判斷是否是有效的文件及文件夾路徑

    如何利用C#正則表達(dá)式判斷是否是有效的文件及文件夾路徑

    項(xiàng)目中少不了讀取或設(shè)置文件路徑的功能,如何才能對(duì)輸入的路徑是否合法進(jìn)行判斷呢?下面這篇文章主要給大家介紹了關(guān)于C#利用正則表達(dá)式判斷是否是有效的文件及文件夾路徑的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • C#?函數(shù)返回多個(gè)值的方法詳情

    C#?函數(shù)返回多個(gè)值的方法詳情

    這篇文章主要介紹了C#函數(shù)返回多個(gè)值的方法詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • C#設(shè)置文件權(quán)限的方法

    C#設(shè)置文件權(quán)限的方法

    這篇文章主要介紹了C#設(shè)置文件權(quán)限的方法,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下
    2020-08-08
  • C#下listview如何插入圖片

    C#下listview如何插入圖片

    這篇文章主要為大家詳細(xì)介紹了C#下listview如何插入圖片,如何在listview中插入圖片的每一個(gè)步驟為大家分享,感興趣的朋友可以參考一下
    2016-05-05
  • 動(dòng)態(tài)webservice調(diào)用接口并讀取解析返回結(jié)果

    動(dòng)態(tài)webservice調(diào)用接口并讀取解析返回結(jié)果

    webservice的 發(fā)布一般都是使用WSDL(web service descriptive language)文件的樣式來(lái)發(fā)布的,在WSDL文件里面,包含這個(gè)webservice暴露在外面可供使用的接口。今天我們來(lái)詳細(xì)討論下如何動(dòng)態(tài)調(diào)用以及讀取解析返回結(jié)果
    2015-06-06
  • 基于C#實(shí)現(xiàn)高效示波器功能

    基于C#實(shí)現(xiàn)高效示波器功能

    這篇文章介紹了用?C#實(shí)現(xiàn)示波器功能的方法,包括使用?WinForm?及多種曲線控件,闡述了原理和思路,如定義緩存數(shù)據(jù)的隊(duì)列、轉(zhuǎn)化數(shù)組刷新顯示等,還提到注意事項(xiàng)及擴(kuò)展特性,最后呼吁點(diǎn)贊支持和交流,需要的朋友可以參考下
    2024-12-12
  • C#實(shí)現(xiàn)撲克游戲(21點(diǎn))的示例代碼

    C#實(shí)現(xiàn)撲克游戲(21點(diǎn))的示例代碼

    21點(diǎn)又名黑杰克,該游戲由2到6個(gè)人玩,使用除大小王之外的52張牌,游戲者的目標(biāo)是使手中的牌的點(diǎn)數(shù)之和不超過(guò)21點(diǎn)且盡量大。本文將用C#實(shí)現(xiàn)這一經(jīng)典游戲,需要的可以參考一下
    2022-08-08
  • C# 通過(guò)NI-VISA操作Tektronix TBS 2000B系列示波器的實(shí)現(xiàn)步驟

    C# 通過(guò)NI-VISA操作Tektronix TBS 2000B系列示波器的實(shí)現(xiàn)步驟

    這篇文章主要介紹了C# 通過(guò)NI-VISA操作Tektronix TBS 2000B系列示波器的實(shí)現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-02-02

最新評(píng)論

鄂托克前旗| 新建县| 安陆市| 宁阳县| 临高县| 平湖市| 平邑县| 马山县| 平邑县| 新源县| 平昌县| 鄢陵县| 南靖县| 砀山县| 广安市| 彭阳县| 北川| 湘阴县| 团风县| 江都市| 柏乡县| 金湖县| 保德县| 吴桥县| 金阳县| 木兰县| 黄浦区| 衡东县| 孟连| 益阳市| 青河县| 嘉禾县| 四会市| 蛟河市| 兰考县| 荔浦县| 蒙城县| 安平县| 上犹县| 新田县| 沙雅县|