.netcore grpc服务端流方法详解

news/2024/5/19 23:06:40 标签: .netcore, rpc

一、服务端流式处理概述

  1. 客户端向服务端发送请求,服务端可以将多个消息流式传输回调用方
  2. 和客户端流相反,客户端流发出请求,服务端可以传输一批消息给客户端,直至本次请求响应完全结束。
  3. 针对文件分段传输下载,该方式非常有用。

二、案例介绍

  1. 提供一个一元方法查询文件
  2. 提供一个文件流传输的服务端流式方法,进行文件流推送

三、服务端配置(注意:grpc相关配置参考我之前的文章)

// 1.提供公共的实体proto文件
// 2.服务引用对应的proto文件
// 3.定义三个客户流方法


syntax = "proto3";

option csharp_namespace = "GrpcProject";

package grpc.serviceing;


// 服务端流对应的请求流和响应流

message FileInfoRequest
{
	string fileName = 1;
}

message FileInfoResponse
{
	string fileName = 1;
	int64 fileSize = 2;
	string extension = 3;
}

message ProgressBarResponse
{
	FileInfoResponse fileMessage = 1;
	bytes fileBytes = 2;
}







// serverstream.proto定义service方法


syntax = "proto3";

import "google/protobuf/empty.proto";
import "Protos/messages.proto";

option csharp_namespace = "GrpcProject";

package grpc.serviceing;

service ServerStreamRpc{
	// 一元文件获取展示
	rpc GetFileMessage(google.protobuf.Empty) returns (FileInfoResponse);
	// 服务端文件流处理
	rpc StreamingFromServer	(FileInfoRequest) returns (stream ProgressBarResponse);
}

服务接口实现:

    public class ServerStreamService : ServerStreamRpc.ServerStreamRpcBase
    {
        /// <summary>
        /// 获取文件信息
        /// </summary>
        /// <param name="request">空请求</param>
        /// <param name="context">服务调用上下文</param>
        /// <returns></returns>
        public override Task<FileInfoResponse> GetFileMessage(Empty request, ServerCallContext context)
        {
            var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Files", "cn-Liang-y.rar");

            FileInfo fileInfo = new FileInfo(filePath);
            FileInfoResponse fileInfoResponse = new FileInfoResponse();
            fileInfoResponse.FileName = fileInfo.Name;
            fileInfoResponse.FileSize = fileInfo.Length;
            fileInfoResponse.Extension = fileInfo.Extension;

            return Task.FromResult(fileInfoResponse);
        }

        public override async Task StreamingFromServer(FileInfoRequest request,
                                                 IServerStreamWriter<ProgressBarResponse> responseStream,
                                                 ServerCallContext context)
        {

            var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Files", request.FileName);
            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException(nameof(filePath));
            }

            // 进度条按照 100百分比进行划分
            FileInfo fileInfo = new FileInfo(filePath);

            using var fileStream = fileInfo.OpenRead();
            // 插入固定长度

            int fixedLength = (int)fileStream.Length / 100;
            byte[] fileBytes = new byte[fixedLength];
            int len;
            while ((len = fileStream.Read(fileBytes, 0, fixedLength)) > 0)
            {
                await Console.Out.WriteLineAsync($"打印字节长度:{len}");
                var response = new ProgressBarResponse();
                response.FileMessage = new FileInfoResponse
                {
                    FileName = fileInfo.Name,
                    FileSize = fileInfo.Length,
                    Extension = fileInfo.Extension
                };
                response.FileBytes = ByteString.CopyFrom(fileBytes);
                await responseStream.WriteAsync(response);
            }

        }
    }

Program注入:

    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            builder.Services.AddGrpc();
            var app = builder.Build();
            // 一元方法
            //app.MapGrpcService<DollarService>();
            // 客户端流
            //app.MapGrpcService<ClientStreamService>();
            // 服务端流
            app.MapGrpcService<ServerStreamService>();
            app.Run();
        }
    }

四、客户端配置

  1. 引用proto文件,配置为客户端类型
  2. 根据编译生成的函数进行传参调用
  3. 创建WPF客户端提供控制条显示

 button按钮触发grpc

        private async void download_Click(object sender, RoutedEventArgs e)
        {
            Action<int> action = async i =>
            {
                progressBar.Value = i;
                await Task.Delay(100);
            };

            await WpfClient.Show(action);
        }

grpc客户端接口调用

    public class WpfClient
    {
        public static async Task Show(Action<int> action)
        {

            var channel = GrpcChannel.ForAddress("https://localhost:7188");


            var client = new GrpcProject.ServerStreamRpc.ServerStreamRpcClient(channel);

            var fileMessage = await client.GetFileMessageAsync(new Google.Protobuf.WellKnownTypes.Empty());

            FileInfoRequest request = new FileInfoRequest();
            request.FileName = fileMessage.FileName;

            var streaming = client.StreamingFromServer(request);

            var path = Path.Combine(Directory.GetCurrentDirectory(), "test.rar");

            using var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
            int i = 0;
            await foreach (var item in streaming.ResponseStream.ReadAllAsync())
            {
                stream.Write(item.FileBytes.Span);
                action(i++);
            }
            stream.Flush();
            stream.Close();
        }
    }

五、执行结果

 在文件根目录可以看到下载的文件

 六、源码地址

链接:https://pan.baidu.com/s/13_AEFHLLJS5qN8aIby8IsA 
提取码:72x0


http://www.niftyadmin.cn/n/4940057.html

相关文章

基于Mybatis Plus的SQL输出拦截器。完美的输出打印 SQL 及执行时长、statement

我们需要想办法打印出完成的SQL&#xff0c;Mybatis为我们提供了 org.apache.ibatis.plugin.Interceptor接口&#xff0c;我们来实现该接口做一些打印SQL的工作 package org.springjmis.core.mp.plugins;import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; impor…

LeetCode 0617. 合并二叉树

【LetMeFly】617.合并二叉树 力扣题目链接&#xff1a;https://leetcode.cn/problems/merge-two-binary-trees/ 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而…

jar包的解压缩和压缩后重新运行,报错:xxx.jar中没有主清单属性

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、手动模拟过程定位问题二、jdk提供的jar包压缩工具总结 前言 我使用插件yauzl把jar包解压缩、修改properties、压缩后&#xff0c;发现新的jar包无法运行&#xff0c;提示&#xff1a;xxx.jar中没有主清单属性 然后通…

【AutoLayout案例03-设置底部按钮之间相同间距 Objective-C语言】

一、好,咱们继续啊 1.咱们继续把autoLayout介绍一下 咱们的自动布局 给大家介绍一下 那么,自动布局呢 继续咱们给大家做的案例 做几个例子 把这几个例子做完以后 我们再给它 我们再给大家说一下,如何通过代码,来实现自动布局 虽然说,通过代码来实现自动布局,并不推荐 但…

品牌推广革新之道:海外网红与内容营销的融合

随着数字时代的来临&#xff0c;品牌推广的方式正在经历着革命性的变化。传统的广告手段逐渐失去了吸引力&#xff0c;而内容营销正成为品牌推广的新宠儿。尤其是海外网红的崛起&#xff0c;不仅改变了推广方式&#xff0c;更重新定义了品牌与消费者之间的互动关系。本文Nox聚星…

vue自定义指令--动态参数绑定

在企业微信侧边栏应用中&#xff0c;给dialog添加了拖拽功能&#xff0c;但是因为dialog高度超过了页面高度&#xff0c;所以高度100%时拖拽有个bug--自动贴到窗口顶部而且企业侧边栏宽高都有限制&#xff0c;拖拽效果并不理想&#xff0c;所以就想缩小dialog再进行拖拽。 拖拽…

Leetcode-每日一题【剑指 Offer 31. 栈的压入、弹出序列】

题目 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如&#xff0c;序列 {1,2,3,4,5} 是某栈的压栈序列&#xff0c;序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列&#xf…

网络安全攻防实战:探索互联网发展史

大家好&#xff0c;我是沐尘而生。 互联网发展史&#xff1a;数字世界的壮阔画卷 从早期的ARPANET到今天的万物互联&#xff0c;互联网经历了漫长的发展过程。然而&#xff0c;随着技术的进步&#xff0c;网络安全问题也随之而来。我们不仅要探索互联网的壮阔历程&#xff0c;…