1.环境要求

.Net6,Visual Studio 2019 以上

官方文档: https://learn.microsoft.com/zh-cn/aspnet/core/tutorials/grpc/grpc-start

Net Framework 版本: https://www.cnblogs.com/dennisdong/p/17119944.html

2.搭建帮助类

2.1 新建类库

GrpcCommon

2.2 新建文件夹

文件夹:Certs,Helpers,Models

2.3 安装依赖

NuGet依赖包Microsoft.AspNetCore.Authentication.JwtBeare 6.0.12,Newtonsoft.Json 13.0.2

2.4 新建项目文件

在Models下新建JwtToken.cs和UserDetails.cs

namespace GrpcCommon.Models
{
public class JwtToken
{
public string? UserId { get; set; }
public string? Exp { get; set; }
public string? Iss { get; set; }
}
}
namespace GrpcCommon.Models
{
public class UserDetails
{
public string? UserName { get; set; }
public int Age { get; set; }
public IEnumerable<string>? Friends { get; set; }
}
}

在Helpers下新建JwtHelper.cs

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json; namespace GrpcCommon.Helpers
{
public class JwtHelper
{
/// <summary>
/// 颁发JWT Token
/// </summary>
/// <param name="securityKey"></param>
/// <param name="accountName"></param>
/// <returns></returns>
public static string GenerateJwt(string securityKey, string accountName)
{
var claims = new List<Claim>
{
new Claim("userid", accountName)
}; //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
issuer: "https://ifcloud.com/zerotrust",
claims: claims,
expires: DateTime.Now.AddMinutes(1),
signingCredentials: credentials);
var jwtHandler = new JwtSecurityTokenHandler();
var encodedJwt = jwtHandler.WriteToken(jwt);
return encodedJwt;
} /// <summary>
/// 解析
/// </summary>
/// <param name="token"></param>
/// <param name="securityKey"></param>
/// <returns></returns>
public static Tuple<bool, string> ValidateJwt(string token, string securityKey)
{
try
{
//对称秘钥
SecurityKey key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey));
//校验token
var validateParameter = new TokenValidationParameters()
{
ValidateAudience = false,
ValidIssuer = "https://ifcloud.com/zerotrust",
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ClockSkew = TimeSpan.Zero//校验过期时间必须加此属性
};
var jwtToken = new JwtSecurityTokenHandler().ValidateToken(token, validateParameter, out _);
var claimDic = new Dictionary<string, string>(); foreach (var claim in jwtToken.Claims)
{
claimDic.TryAdd(claim.Type, claim.Value);
} var payLoad = JsonConvert.SerializeObject(claimDic); return new Tuple<bool, string>(true, payLoad);
}
catch (SecurityTokenExpiredException expired)
{
//token过期
return new Tuple<bool, string>(false, expired.Message);
}
catch (SecurityTokenNoExpirationException noExpiration)
{
//token未设置过期时间
return new Tuple<bool, string>(false, noExpiration.Message);
}
catch (SecurityTokenException tokenEx)
{
//表示token错误
return new Tuple<bool, string>(false, tokenEx.Message);
}
catch (Exception err)
{
// 解析出错
Console.WriteLine(err.StackTrace);
return new Tuple<bool, string>(false, err.Message);
}
}
}
}

3.生成SSL证书(可跳过)

3.1 下载安装openssl

参考文章:https://www.cnblogs.com/dingshaohua/p/12271280.html

3.2 生成证书密钥

在GrpcCommon的Certs下右键打开命令窗口输入openssl

genrsa -out key.pem 2048

3.3 生成pem证书

req -new -x509 -key key.pem -out cert.pem -days 3650

3.4 pem证书转换成pfx证书

pkcs12 -export -out cert.pfx -inkey key.pem -in cert.pem

4.搭建grpc服务器

4.1 新建grpc服务

GrpcServer

4.2 新建文件夹

文件夹:Protos及其子文件夹Google

4.3 下载google protobuf文件

https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protoc-21.12-win64.zip

其他版本参考:https://github.com/protocolbuffers/protobuf/releases

下载不了的文章末尾有源码地址

下载解压后将\include\google\protobuf中的所有文件放在Protos下的Google中

4.4 新建proto文件

在Protos下新建文件example.proto

syntax = "proto3";

package example;
import "Protos/Google/struct.proto"; option csharp_namespace = "GrpcExample"; service ExampleServer {
// Unary
rpc UnaryCall (ExampleRequest) returns (ExampleResponse); // Server streaming
rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse); // Client streaming
rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse); // Bi-directional streaming
rpc StreamingBothWays (stream ExampleRequest) returns (stream ExampleResponse);
} message ExampleRequest {
string securityKey = 1;
string userId = 2;
google.protobuf.Struct userDetail = 3;
string token = 4;
} message ExampleResponse {
int32 code = 1;
bool result = 2;
string message = 3;
}

4.5 编译生成Stub

GrpcServer项目右键编辑项目文件添加内容

<ItemGroup>
<Protobuf Include="Protos\example.proto" GrpcServices="Server" />
</ItemGroup>

4.6 添加ssl证书(可跳过)

修改Program.cs

builder.WebHost
.ConfigureKestrel(serviceOpt =>
{
var httpPort = builder.Configuration.GetValue<int>("port:http");
var httpsPort = builder.Configuration.GetValue<int>("port:https");
serviceOpt.Listen(IPAddress.Any, httpPort, opt => opt.UseConnectionLogging());
serviceOpt.Listen(IPAddress.Any, httpsPort, listenOpt =>
{
var enableSsl = builder.Configuration.GetValue<bool>("enableSsl");
if (enableSsl)
{
listenOpt.UseHttps("Certs\\cert.pfx", "1234.com");
}
else
{
listenOpt.UseHttps();
} listenOpt.UseConnectionLogging();
});
});

修改appsettings.json,添加配置项

  "port": {
"http": 5000,
"https": 7000
},
"enableSsl": true

4.7 新建服务类

ExampleService

using Grpc.Core;
using GrpcCommon.Helpers;
using GrpcCommon.Models;
using GrpcExampleServer;
using Newtonsoft.Json; namespace GrpcServer.Services
{
public class ExampleService : ExampleServer.ExampleServerBase
{
private readonly ILogger<ExampleService> _logger; public ExampleService(ILogger<ExampleService> logger)
{
_logger = logger;
} public override Task<ExampleResponse> UnaryCall(ExampleRequest request, ServerCallContext context)
{
Console.WriteLine(request.ToString());
_logger.LogInformation(request.ToString());
var tokenRes = JwtHelper.ValidateJwt(request.Token, request.SecurityKey); // 正常响应客户端一次
ExampleResponse result; if (tokenRes.Item1)
{
var payLoad = JsonConvert.DeserializeObject<JwtToken>(tokenRes.Item2);
if (payLoad == null)
{
result = new ExampleResponse
{
Code = -1,
Result = false,
Message = "payLoad为空"
};
}
else
{
if (!request.UserId.Equals(payLoad.UserId))
{
result = new ExampleResponse
{
Code = -1,
Result = false,
Message = "userid不匹配"
};
}
else
{
var userDetail = JsonConvert.DeserializeObject<UserDetails>(request.UserDetail.Fields.ToString());
result = new ExampleResponse
{
Code = 200,
Result = true,
Message = $"UnaryCall 单次响应: {request.UserId},{userDetail?.UserName}"
};
}
}
}
else
{
// 正常响应客户端一次
result = new ExampleResponse
{
Code = -1,
Result = false,
Message = tokenRes.Item2
};
}
return Task.FromResult(result);
} public override async Task StreamingFromServer(ExampleRequest request, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
// 无限响应客户端
while (!context.CancellationToken.IsCancellationRequested)
{
await responseStream.WriteAsync(new ExampleResponse
{
Code = 200,
Result = true,
Message = $"StreamingFromServer 无限响应: {Guid.NewGuid()}"
});
await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
}
} public override async Task<ExampleResponse> StreamingFromClient(IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
{
// 处理请求
await foreach (var req in requestStream.ReadAllAsync())
{
Console.WriteLine(req.UserId);
} // 响应客户端
return new ExampleResponse
{
Code = 200,
Result = true,
Message = $"StreamingFromClient 单次响应: {Guid.NewGuid()}"
};
} public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
// 服务器响应客户端一次
// 处理请求
//await foreach (var req in requestStream.ReadAllAsync())
//{
// Console.WriteLine(req.UserName);
//} // 请求处理完成之后只响应一次
//await responseStream.WriteAsync(new ExampleResponse
//{
// Code = 200,
// Result = true,
// Message = $"StreamingBothWays 单次响应: {Guid.NewGuid()}"
//});
//await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken); // 服务器响应客户端多次
// 处理请求
var readTask = Task.Run(async () =>
{
await foreach (var req in requestStream.ReadAllAsync())
{
Console.WriteLine(req.UserId);
}
}); // 请求未处理完之前一直响应
while (!readTask.IsCompleted)
{
await responseStream.WriteAsync(new ExampleResponse
{
Code = 200,
Result = true,
Message = $"StreamingBothWays 请求处理完之前的响应: {Guid.NewGuid()}"
});
await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
} // 也可以无限响应客户端
//while (!context.CancellationToken.IsCancellationRequested)
//{
// await responseStream.WriteAsync(new ExampleResponse
// {
// Code = 200,
// Result = true,
// Message = $"StreamingFromServer 无限响应: {Guid.NewGuid()}"
// });
// await Task.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
//}
}
}
}

5.搭建grpc客户端

5.1 新建控制台程序

GrpcClient

5.2 拷贝文件夹

将GrpcServer下的Protos拷贝一份到GrpcClient中

5.3 安装依赖包

Google.Protobuf 3.21.12,Grpc.Net.Client 2.51.0,Grpc.Tools 2.51.0,Newtonsoft.Json 13.0.2

5.4 编译生成Stub

GrpcServer项目右键编辑项目文件添加内容,注意这里是Client

<ItemGroup>
<Protobuf Include="Protos\example.proto" GrpcServices="Client" />
</ItemGroup>

5.5 新建测试类

ExampleTest.cs

using System.Security.Cryptography.X509Certificates;
using Grpc.Net.Client;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using GrpcCommon.Helpers;
using GrpcExample; namespace GrpcClient.Test
{
internal class ExampleTest
{
public static void Run()
{
// 常规请求响应
UnaryCall(); // 服务器流响应
StreamingFromServer(); // 客户端流响应
StreamingFromClient(); // 双向流响应
StreamingBothWays();
} /// <summary>
/// 创建客户端链接
/// </summary>
/// <param name="enableSsl"></param>
/// <returns></returns>
private static ExampleServer.ExampleServerClient CreateClient(bool enableSsl = true)
{
GrpcChannel channel;
if (enableSsl)
{
const string serverUrl = "https://localhost:7000";
Console.WriteLine($"尝试链接服务器,{serverUrl}"); var handler = new HttpClientHandler();
// 添加证书
handler.ClientCertificates.Add(new X509Certificate2("Certs\\cert.pfx", "1234.com")); // 忽略证书
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
channel = GrpcChannel.ForAddress(serverUrl, new GrpcChannelOptions
{
HttpClient = new HttpClient(handler)
});
}
else
{
const string serverUrl = "http://localhost:5000";
Console.WriteLine($"尝试链接服务器,{serverUrl}");
channel = GrpcChannel.ForAddress(serverUrl);
} Console.WriteLine("服务器链接成功");
return new ExampleServer.ExampleServerClient(channel);
} private static async void UnaryCall()
{
var client = CreateClient();
const string securityKey = "Dennis!@#$%^123456.com";
var userId = Guid.NewGuid().ToString();
var token = JwtHelper.GenerateJwt(securityKey, userId);
var result = await client.UnaryCallAsync(new ExampleRequest
{
SecurityKey = securityKey,
UserId = "Dennis",
UserDetail = new Struct
{
Fields =
{
["userName"] = Value.ForString("Dennis"),
["age"] = Value.ForString("18"),
["friends"] = Value.ForList(new Value
{
ListValue = new ListValue
{
Values =
{
new List<Value>
{
Value.ForString("Roger"),
Value.ForString("YueBe")
}
}
}
})
}
},
Token = token
});
Console.WriteLine($"Code={result.Code},Result={result.Result},Message={result.Message}");
} private static async void StreamingFromServer()
{
var client = CreateClient();
var result = client.StreamingFromServer(new ExampleRequest
{
UserId = "Dennis"
}); await foreach (var resp in result.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
}
} private static async void StreamingFromClient()
{
var client = CreateClient();
var result = client.StreamingFromClient(); // 发送请求
for (var i = 0; i < 5; i++)
{
await result.RequestStream.WriteAsync(new ExampleRequest
{
UserId = $"StreamingFromClient 第{i}次请求"
});
await Task.Delay(TimeSpan.FromSeconds(1));
} // 等待请求发送完毕
await result.RequestStream.CompleteAsync(); var resp = result.ResponseAsync.Result;
Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
} private static async void StreamingBothWays()
{
var client = CreateClient();
var result = client.StreamingBothWays(); // 发送请求
for (var i = 0; i < 5; i++)
{
await result.RequestStream.WriteAsync(new ExampleRequest
{
UserId = $"StreamingBothWays 第{i}次请求"
});
await Task.Delay(TimeSpan.FromSeconds(1));
} // 处理响应
var respTask = Task.Run(async () =>
{
await foreach (var resp in result.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Code={resp.Code},Result={resp.Result},Message={resp.Message}");
}
}); // 等待请求发送完毕
await result.RequestStream.CompleteAsync(); // 等待响应处理
await respTask;
}
}
}

5.6 修改程序入口

Program.cs

using GrpcClient.Test;
using Microsoft.Extensions.Hosting; // Example测试
ExampleTest.Run(); Console.WriteLine("==================");
Console.WriteLine("按Ctrl+C停止程序");
Console.WriteLine("=================="); // 监听Ctrl+C
await new HostBuilder().RunConsoleAsync();

6.运行项目

6.1 拷贝证书

把整个Certs文件夹分别拷贝到GrpcServer和GrpcClient下的\bin\Debug\Certs

6.2 启动程序

先运行GrpcServer在运行GrpcClient即可

6.3 调试

右键解决方案-->属性-->启动项目-->选择多个启动项目-->F5调试即可

7.源码地址

https://gitee.com/dennisdong/net-grpc

.Net Core(.Net6)创建grpc的更多相关文章

  1. .NET Core(.NET6)中gRPC使用

    一.简介 简单解析一下gRPC,gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架. 特点: 跨语言 内容protobuf格式(比json体积小),网络传输快 使用HT ...

  2. [gRPC] 在 .NET Core 中创建 gRPC 服务端和客户端

    gRPC 官网:https://grpc.io/ 1. 创建服务端 1.1 基于 ASP.NET Core Web 应用程序模板创建 gRPC Server 项目. 1.2 编译并运行 2. 创建客户 ...

  3. .NET Core(.NET6)中gRPC注册到Consul

    一.简介 上一篇文章介绍了.NET Core 中使用gRPC,在微服务中,我们通常要把服务做成服务注册,服务发现的方式,那么这里来说一下gRPC是如何注册到Consul中的. Consul的安装这里就 ...

  4. ASP.NET Core 3.0 gRPC 双向流

    目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 认证授权 一.前言 在前一文 <ASP.NE ...

  5. ASP.NET Core 3.0 gRPC 配置使用HTTP

    前言 gRPC是基于http/2,是同时支持https和http协议的,我们在gRPC实际使用中,在内网通讯场景下,更多的是走http协议,达到更高的效率,下面介绍如何在 .NET Core 3.0 ...

  6. .Net Core中使用Grpc

    一.Grpc概述 gRPC 基于如下思想:定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型.gRPC 默认使用protocol buffers作为接口定义语言,来描述服务接口和有效载荷消息 ...

  7. 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】

    Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...

  8. 使用 ASP.NET Core MVC 创建 Web API(五)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

  9. 使用 ASP.NET Core MVC 创建 Web API(二)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 六.添加数据库上下文 数据库上下文是使用Entity Framewor ...

  10. 使用 ASP.NET Core MVC 创建 Web API(三)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 十 ...

随机推荐

  1. 2022春每日一题:Day 32

    题目:[USACO12DEC]First! G 不太记得当时怎么想的了,但是显然,当一个字符串的前缀存在则他一定不是first,然后做法:对于每个字符串,把每个字符结尾跟他有相同前缀的单词的同元素建边 ...

  2. 重要内置函数、常见内置函数、可迭代对象、迭代器对象、for循环的本质、异常捕获处理

    重要内置函数 #zip拉链 zip 函数是可以接收多个可迭代对象,然后把每个可迭代对象中的第i个元素组合在一起,形成一个新的迭代器,类型为元组. l1 = [11, 22, 33] l2 = ['a' ...

  3. 重新认识下JVM级别的本地缓存框架Guava Cache——优秀从何而来

    大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 不知不觉,这已经是<深入理解缓存 ...

  4. 2.5:Python常用内置数据结构、多维数组ndarray、Series和DataFrame

    一.Python内置数据结构 1.赋值生成列表 la=[1,2,3,4] la 2.强制转换为列表 lb=list("Hello") lb 3.推导式生成列表 s="ab ...

  5. 1.4 Apache Hadoop完全分布式集群搭建-hadoop-最全最完整的保姆级的java大数据学习资料

    目录 1.4 Apache Hadoop 完全分布式集群搭建 1.4.1 虚拟机环境准备 1.4.2 集群规划 1.4.3 安装Hadoop 1.4.3.1 集群配置 1.4.3.1.1 HDFS集群 ...

  6. 干电池升压IC或者干电池升压芯片

    1, 干电池升压IC                            升压输出3V,3,3V,5V等3V-5V可调 2, 单节锂电池升压IC                     升压输出4. ...

  7. 说说真实Java项目的开发流程,以及面试前的项目准备说辞

    介绍项目是必不可少的Java面试环节,求职者需要借此证明自己真实Java项目的经验,如果再做的好的话,需要借此展开自己的亮点说辞. 不过之前如果只有学习项目经验,比如是自己跑通一个项目,或者是在培训班 ...

  8. Blazor组件自做十四 : Blazor FileViewer 文件预览 组件

    Blazor FileViewer 文件预览 组件 目前支支持 Excel(.docx) 和 Word(.xlsx) 格式 示例: https://www.blazor.zone/fileViewer ...

  9. @Data加在子类上,子类无法获取父类的属性

    1.问题描述 我的子类继承父类,并在子类上加了@Data注解.但在程序运行时,输出的结果只有我在子类中定义的属性,父类的属性没有输出. 这是我定义的子类: 这个是子类继承的父类: 这个是输出结果: 可 ...

  10. linux硬盘分区挂载

    这里使用手动挂载方式,还有其他挂载方式请百度 检查网站的磁盘状态,确认是否有没有分区的磁盘. fdisk -l 如图所示,这个服务器有两个硬盘第一个299.0G,第二个20000.0G.这种情况说明硬 ...