.NetCore gRpc 客户端与服务端的单工通信Demo

news/2024/9/15 20:51:16/

文章目录

  • .NetCore gRpc 客户端与服务端的单工通信Demo
    • 服务端
      • 方式一
      • 方式二
    • 客户端
    • proto协议文件
      • syntax = "proto3";
      • import "google/protobuf/empty.proto";
      • service
      • proto3与.netCore 的类型对应
      • 日期和时间
      • 可为 null 的类型
      • 字节
      • 小数
        • 为 Protobuf 创建自定义 decimal 类型
    • 集合
      • 列表
      • 字典
    • 无结构的条件消息
      • 任意
      • Oneof
      • “值”

.NetCore gRpc 客户端与服务端的单工通信Demo

服务端

方式一

使用vs 2022(也可以是其他版本)创建一个grpc的服务,如下这样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uipEG9Xu-1687172462785)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230619183828284.png)]

简单方便,创建项目后的目录结构如下图

  • Protos:协议目录,里面是grpc的协议文件 默认是greet.proto
  • Services:服务目录,对服务功能进行重写,默认文件是 GreeterService.cs

方式二

  • 新建一个控制台程序,引入nuget包(版本根据实际情况确定)

  • 新建一个Protos目录(当然也可以是其他的),新建一个文本文件命名为xxxxx.proto

  • 编辑xxxx.proto文件(具体格式下面介绍)

  • 鼠标右键项目,添加–>服务引用–>gRPC–>选择文件(刚刚的那个proto文件)–>生成类型选择服务器,如下图

在这里插入图片描述

  • 点击完成

  • 新建一个Service.cs类,继承自生成的类后,重写处理方法(如果没有生成就先编译一下工程文件)

      public class GreeterService : Greeter.GreeterBase{private readonly ILogger<GreeterService> _logger;public GreeterService(ILogger<GreeterService> logger){_logger = logger;}public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context){Console.WriteLine($"接收到请求!{request.Name}");return Task.FromResult(new HelloReply{Message = "Hello " + request.Name});}public override Task<Empty> SayBye(Empty request, ServerCallContext context){return base.SayBye(request, context);}}
    

    注意命名空间

客户端

  • 新建一个控制台

  • 添加引用服务 grpc,选择客户端(具体操作和上面的生成服务端的类似)

  • 代码如下

     internal class Program{static async Task Main(string[] args){Console.WriteLine("Hello, World!");var channel = GrpcChannel.ForAddress("https://localhost:6001"); var client = new Greeter.GreeterClient(channel);var response = await client.SayHelloAsync(new HelloRequest { Name = "张三 李四 王五" });Console.WriteLine(response.Message);}}
    

    运行两个程序就可以了。

proto协议文件

https://developers.google.com/protocol-buffers/docs/proto

上面的连接有详细说明,下面简单介绍一些基本使用

syntax = "proto3";import "google/protobuf/empty.proto";option csharp_namespace = "Demo_WarningMonitor.OpcUA.Client";package greet;// The greeting service definition.
service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply);rpc SayBye(google.protobuf.Empty) returns (google.protobuf.Empty);
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings.
message HelloReply {string message = 1;
}

syntax = “proto3”;

声明协议语法是proto3

import “google/protobuf/empty.proto”;

导入一些默认的类型

service

定义一个服务,内部使用rpc关键字指定方法描述

proto3与.netCore 的类型对应

Protobuf 支持一系列本机标量值类型。 下表列出了全部本机标量值类型及其等效 C# 类型:

Protobuf 类型C# 类型
doubledouble
floatfloat
int32int
int64long
uint32uint
uint64ulong
sint32int
sint64long
fixed32uint
fixed64ulong
sfixed32int
sfixed64long
boolbool
stringstring
bytesByteString

标量值始终具有默认值,并且该默认值不能设置为 null。 此约束包括 stringByteString,它们都属于 C# 类。 string 默认为空字符串值,ByteString 默认为空字节值。 尝试将它们设置为 null 会引发错误。

可为 null 的包装器类型可用于支持 null 值。

日期和时间

本机标量类型不提供与 .NET 的 DateTimeOffset、DateTime 和 TimeSpan 等效的日期和时间值。 可使用 Protobuf 的一些“已知类型”扩展来指定这些类型。 这些扩展为受支持平台中的复杂字段类型提供代码生成和运行时支持。

下表显示日期和时间类型:

.NET 类型Protobuf 已知类型
DateTimeOffsetgoogle.protobuf.Timestamp
DateTimegoogle.protobuf.Timestamp
TimeSpangoogle.protobuf.Duration

ProtoBuf复制

syntax = "proto3";import "google/protobuf/duration.proto";  
import "google/protobuf/timestamp.proto";message Meeting {string subject = 1;google.protobuf.Timestamp start = 2;google.protobuf.Duration duration = 3;
}  

可为 null 的类型

C# 的 Protobuf 代码生成使用本机类型,如 int 表示 int32。 因此这些值始终包括在内,不能为 null

对于需要显式 null 的值(例如在 C# 代码中使用 int?),Protobuf 的“已知类型”包括编译为可以为 null 的 C# 类型的包装器。 若要使用它们,请将 wrappers.proto 导入到 .proto 文件中,如以下代码所示:

ProtoBuf复制

syntax = "proto3";import "google/protobuf/wrappers.proto";message Person {// ...google.protobuf.Int32Value age = 5;
}

wrappers.proto 类型不会在生成的属性中公开。 Protobuf 会自动将它们映射到 C# 消息中相应的可为 null 的 .NET 类型。 例如,google.protobuf.Int32Value 字段生成 int? 属性。 引用类型属性(如 stringByteString )保持不变,但可以向它们分配 null,这不会引发错误。

下表完整列出了包装器类型以及它们的等效 C# 类型:

C# 类型已知类型包装器
bool?google.protobuf.BoolValue
double?google.protobuf.DoubleValue
float?google.protobuf.FloatValue
int?google.protobuf.Int32Value
long?google.protobuf.Int64Value
uint?google.protobuf.UInt32Value
ulong?google.protobuf.UInt64Value
stringgoogle.protobuf.StringValue
ByteStringgoogle.protobuf.BytesValue

字节

Protobuf 支持标量值类型为 bytes 的二进制有效负载。 C# 中生成的属性使用 ByteString 作为属性类型。

使用 ByteString.CopyFrom(byte[] data) 从字节数组创建新实例:

var data = await File.ReadAllBytesAsync(path);var payload = new PayloadResponse();
payload.Data = ByteString.CopyFrom(data);

使用 ByteString.SpanByteString.Memory 直接访问 ByteString 数据。 或调用 ByteString.ToByteArray() 将实例转换回字节数组:

var payload = await client.GetPayload(new PayloadRequest());await File.WriteAllBytesAsync(path, payload.Data.ToByteArray());

小数

Protobuf 本身不支持 .NET decimal 类型,只支持 doublefloat。 在 Protobuf 项目中,我们正在探讨这样一种可能性:将标准 decimal 类型添加到已知类型,并为支持它的语言和框架添加平台支持。 尚未实现任何内容。

可以创建消息定义来表示 decimal 类型,以便在 .NET 客户端和服务器之间实现安全序列化。 但其他平台上的开发人员必须了解所使用的格式,并能够实现自己对其的处理。

为 Protobuf 创建自定义 decimal 类型

ProtoBuf复制

package CustomTypes;// Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
message DecimalValue {// Whole units part of the amountint64 units = 1;// Nano units of the amount (10^-9)// Must be same sign as unitssfixed32 nanos = 2;
}

nanos 字段表示从 0.999_999_999-0.999_999_999 的值。 例如,decimal1.5m 将表示为 { units = 1, nanos = 500_000_000 }。 这就是此示例中的 nanos 字段使用 sfixed32 类型的原因:对于较大的值,其编码效率比 int32 更高。 如果 units 字段为负,则 nanos 字段也应为负。

集合

列表

Protobuf 中,在字段上使用 repeated 前缀关键字指定列表。 以下示例演示如何创建列表:

message Person {// ...repeated string roles = 8;
}

在生成的代码中,repeated 字段由 Google.Protobuf.Collections.RepeatedField<T> 泛型类型表示。

public class Person
{// ...public RepeatedField<string> Roles { get; }
}

RepeatedField<T> 可实现 IList。 因此你可使用 LINQ 查询,或者将其转换为数组或列表。 RepeatedField<T> 属性没有公共 setter。 项应添加到现有集合中。

var person = new Person();// Add one item.
person.Roles.Add("user");// Add all items from another collection.
var roles = new [] { "admin", "manager" };
person.Roles.Add(roles);

字典

.NET IDictionary 类型在 Protobuf 中使用 map<key_type, value_type> 表示。

message Person {// ...map<string, string> attributes = 9;
}

在生成的 .NET 代码中,map 字段由 Google.Protobuf.Collections.MapField<TKey, TValue> 泛型类型表示。 MapField<TKey, TValue> 可实现 IDictionary。 与 repeated 属性一样,map 属性没有公共 setter。 项应添加到现有集合中。

var person = new Person();// Add one item.
person.Attributes["created_by"] = "James";// Add all items from another collection.
var attributes = new Dictionary<string, string>
{["last_modified"] = DateTime.UtcNow.ToString()
};
person.Attributes.Add(attributes);

无结构的条件消息

Protobuf 是一种协定优先的消息传递格式。 构建应用时,必须在 .proto 文件中指定应用的消息,包括其字段和类型。 Protobuf 的协定优先设计非常适合强制执行消息内容,但可能会限制不需要严格协定的情况:

  • 包含未知有效负载的消息。 例如,具有可以包含任何消息的字段的消息。
  • 条件消息。 例如,从 gRPC 服务返回的消息可能是成功结果或错误结果。
  • 动态值。 例如,具有包含非结构化值集合的字段的消息,类似于 JSON。

Protobuf 提供语言功能和类型来支持这些情况。

任意

利用 Any 类型,可以将消息作为嵌入类型使用,而无需 .proto 定义。 若要使用 Any 类型,请导入 any.proto

import "google/protobuf/any.proto";message Status {string message = 1;google.protobuf.Any detail = 2;
}
// Create a status with a Person message set to detail.
var status = new ErrorStatus();
status.Detail = Any.Pack(new Person { FirstName = "James" });// Read Person message from detail.
if (status.Detail.Is(Person.Descriptor))
{var person = status.Detail.Unpack<Person>();// ...
}

Oneof

oneof 字段是一种语言特性。 编译器在生成消息类时处理 oneof 关键字。 使用 oneof 指定可能返回 PersonError 的响应消息可能如下所示:

message Person {// ...
}message Error {// ...
}message ResponseMessage {oneof result {Error error = 1;Person person = 2;}
}

在整个消息声明中,oneof 集内的字段必须具有唯一的字段编号。

使用 oneof 时,生成的 C# 代码包括一个枚举,用于指定哪些字段已设置。 可以测试枚举来查找已设置的字段。 未设置的字段将返回 null 或默认值,而不是引发异常。

var response = await client.GetPersonAsync(new RequestMessage());switch (response.ResultCase)
{case ResponseMessage.ResultOneofCase.Person:HandlePerson(response.Person);break;case ResponseMessage.ResultOneofCase.Error:HandleError(response.Error);break;default:throw new ArgumentException("Unexpected result.");
}

“值”

Value 类型表示动态类型的值。 它可以是 null、数字、字符串、布尔值、值字典 (Struct) 或值列表 (ValueList)。 Value 是一个 Protobuf 已知类型,它使用前面讨论的 oneof 功能。 若要使用 Value 类型,请导入 struct.proto

import "google/protobuf/struct.proto";message Status {// ...google.protobuf.Value data = 3;
}
// Create dynamic values.
var status = new Status();
status.Data = Value.ForStruct(new Struct
{Fields ={["enabled"] = Value.ForBool(true),["metadata"] = Value.ForList(Value.ForString("value1"),Value.ForString("value2"))}
});// Read dynamic values.
switch (status.Data.KindCase)
{case Value.KindOneofCase.StructValue:foreach (var field in status.Data.StructValue.Fields){// Read struct fields...}break;// ...
}

直接使用 Value 可能很冗长。 使用 Value 的替代方法是通过 Protobuf 的内置支持,将消息映射到 JSON。 Protobuf 的 JsonFormatterJsonWriter 类型可用于任何 Protobuf 消息。 Value 特别适用于与 JSON 进行转换。

以下是与之前的代码等效的 JSON:

// Create dynamic values from JSON.
var status = new Status();
status.Data = Value.Parser.ParseJson(@"{""enabled"": true,""metadata"": [ ""value1"", ""value2"" ]
}");// Convert dynamic values to JSON.
// JSON can be read with a library like System.Text.Json or Newtonsoft.Json
var json = JsonFormatter.Default.Format(status.Data);
var document = JsonDocument.Parse(json);

https://learn.microsoft.com/zh-cn/dotnet/architecture/grpc-for-wcf-developers/migrate-duplex-services

https://learn.microsoft.com/zh-cn/aspnet/core/grpc/protobuf?view=aspnetcore-7.0

https://blog.csdn.net/iml6yu/article/details/102948188?ops_request_misc=&request_id=45879ab70342436ea16723b29630d5e4&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2blogkoosearch~default-1-102948188-null-null.268v1control&utm_term=Grpc&spm=1018.2226.3001.4450

https://blog.csdn.net/iml6yu/article/details/102959674?ops_request_misc=&request_id=45879ab70342436ea16723b29630d5e4&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2blogkoosearch~default-2-102959674-null-null.268v1control&utm_term=Grpc&spm=1018.2226.3001.4450


http://www.ppmy.cn/news/470815.html

相关文章

6 从0开始学PyTorch | 构建模型、损失函数、广播机制

前面都在学一些PyTorch的基本操作&#xff0c;从这一节开始&#xff0c;真正进入到模型训练的环节了。原作者很贴心的一步步教我们实现训练步骤&#xff0c;并且还从一个最简单的例子出发&#xff0c;讲了优化方案。 宏观上的训练过程 image.png 当然这里所说的训练还没有到深…

LCD液晶屏和LED液晶屏的较量

一、LED定义   LED是发光二极管Light Emitting Diode的英文缩写。LED应用可分为两大类&#xff1a;一是LED显示屏&#xff1b;二是LED单管应用&#xff0c;包括背光源 LED&#xff0c;红外线LED等。现在就LED显示屏而言&#xff0c;中国的设计和生产技术水平基本与国际同步。L…

解读LED液晶屏

首先&#xff0c;大家熟悉的“LCD”是液晶显示器&#xff08;Liquid Crystal Display&#xff09;的英文缩写。市场上不论台式机还是笔记本电脑的显示器都是“LCD”。 但是“LCD”的背光技术却大有不同—— 目前市面上大部分LCD是采用“CCFL”背光技术的LCD&#xff1b;而“LED…

液晶屏LED背光板可以分为几类?

背光板是液晶显示器产品中提供背光光源的光学元件。由于液晶玻璃本身没有发光特性&#xff0c;所以将根据需要在液晶玻璃后面添加发光源&#xff0c;即背光板。应用范围涵盖医疗、美容、仪器仪表、车载设备、工业设备、银行终端、办公自动化、通信、电子玩具、消费类产品。 1&a…

package.json和package-lock.json依赖包文件

一、package.json package.json 是在运行 “ npm init ”时生成的&#xff0c;主要记录项目依赖 name&#xff1a;项目名&#xff0c;也就是在使用npm init 初始化时取的名字&#xff0c;但是如果使用的是npm init -y 快速初始化的话&#xff0c;那这里的名字就是默认存放这个…

液晶屏背光LED驱动电路设计参考

最近很多朋友因为液晶屏背光驱动在烦恼&#xff0c;我总结一下。 市面上TFT液晶屏使用的时候&#xff0c;都需要将背光LED点亮&#xff0c;液晶显示的内容才看得到&#xff0c;因为TFT属于被动式显示。 液晶屏常用的背光LED灯驱动方式有3这种&#xff1a; 1.并联型&#xff08…

Java实体间转换并导出excel

两个实体间转换导出excel 首先建两个实体 public class Order { private Long id; private String orderNo; private String customerName; private Date createTime; // getter 和 setter 方法 } public class OrderVO { private String orderNo; private String customerNam…

操作系统期末复习简记(更新中~)

文件 定义&#xff1a;文件是以计算机硬盘为载体的存储在计算机上的信息集合&#xff08;宽泛的&#xff09; 属性&#xff1a;描述文件状态的信息&#xff0c;eg.名称&#xff0c;修改时间等等 基本操作&#xff1a;创建、打开、修改文件 文件的逻辑结构 1、无结构文件&#x…

C语言结构体计算大小结构体的对齐数,修改默认对齐数。

结构体的对齐规则 第一个成员在与结构体变量偏移量为0的地址处。剩余成员变量要对齐到对齐数的整数倍的地址处。 对齐数&#xff1a;编译器默认对齐数与该成员大小的较小值vs中默认对齐数为8没有默认对齐数&#xff0c;那么对齐数就是其本身成员大小 结构体总大小为最大对齐数…

优酷视频怎么转二维码_优酷视频转二维码

有的人想将自己制作的视频分享出去&#xff0c;那么该如何将视频转换成二维码来分享呢?其实方法很简单&#xff0c;直接在视频界面中就能生成&#xff0c;这里和大家讲讲。 01、 登录自己的优酷帐号&#xff0c;然后点击右上角的上传按钮。 优酷视频怎么转二维码_优酷视频转…

优酷kux怎么转码mp4

2021年3月3日实测可行。 可以借助优酷自带的解码器进行kux转码成mp4&#xff0c;以下方法能转1080P及4K非独播的剧&#xff0c;大家可以新建一个文本文档然后把以下文字复制进去然后另存为bat&#xff0c;这个bat只要在kux所在目录运行就可以将所有kux自动转换成mp4&#xff0…

Vue2 插槽

1.什么是插槽 插槽(Slot)是vue为组件的封装者提供能力。允许开发者在封装组件时&#xff0c;把不确定的&#xff0c;希望由用户指定的部分定义为插槽。 <template><div id""><h1>App根组件</h1><!-- Left组件和Right组件渲染区域 -->&…

解决耳机插电脑 只有背景音乐

打开控制面板 点击 管理音频 将扬声器设为默认&#xff0c;如果是的话跳过这一步 打开扬声器属性 点击 级别 — 平衡 播放音乐&#xff0c;调节左前&#xff0c;右前 调整到最舒服的位置&#xff0c;确定退出就可以了 PS&#xff1a;如果有帮助&#xff0c;记得点赞哦&#xf…

Vue2系列教程——插槽

Vue2 插槽 1. 默认插槽 App.vue文件中&#xff0c; <template><div><Category title"电影" :listData"films"><img src"../img/007.jpg" alt""></Category></div> </template>Category文…

vue组件嵌套(初学者超简单,一看就懂)

一.组件的创建模板&#xff1a; //第一步&#xff1a;创建定义组件 const 定义的组件名Vue.extend({template:<div>组件的内容&#xff08;html标签插值语法&#xff09;</div>&#xff0c;data(){ //必须写为data(){}函数方法格式组件内容中插值语法的数据} }…

TPS和QPS定义以及影响TPS的因素

一、TPS:每秒传输的事务处理个数,即服务器每秒处理的事务数。TPS包括一条消息入和一条消息出,加上一次用户数据库访问。 TPS是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束…

Vue框架插槽(第八课)

案例 组件信息的通信 自己思考一下 答案在本文章的后面 插槽 v-slot 这个时候我们就可以来定义插槽slot&#xff1a; 插槽的使用过程其实是抽取共性、预留不同&#xff1b;我们会将共同的元素、内容依然在组件内进行封装&#xff1b;同时会将不同的元素使用slot作为占位&#x…

【vue2】插槽的介绍、分类与使用

一、前言 插槽在开发中的使用频率不算低&#xff0c;插槽的使用能帮助我们更好的个性化开发。 二、分类 1、默认插槽 在子组件中定义<slot></slot>即可 2、具名插槽 在子组件中定义<slot name"xxx"></slot>&#xff0c;其中xxx即为插槽…

【Vue3】如何使用插槽

默认插槽 插槽可以让父组件给子组件传递模板片段 在父组件中&#xff0c;在组件内编写模板片段传递给子组件在子组件中&#xff0c;编写 slot 元素&#xff0c;指定模板片段输出的位置 插槽内容可以访问到父组件的数据&#xff0c;但无法访问到子组件的数据。因为插槽本身是…

Vue之插槽

1. 插槽是什么 插槽就是子组件中的提供给父组件使用的一个占位符&#xff0c;用<slot></slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&#xff0c;填充的内容会替换子组件的标签。简单理解就是子组件中留下个“坑…