VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • C#语言下使用gRPC、protobuf(Google Protocol Buffers)实现文件传输

初识gRPC还是一位做JAVA的同事在项目中用到了它,为了C#的客户端程序和java的服务器程序进行通信和数据交换,当时还是对方编译成C#,我直接调用。

  后来,自己下来做了C#版本gRPC编写,搜了很多资料,但许多都是从入门开始?调用说“Say Hi!”这种官方标准的入门示例,然后遇到各种问题……

  关于gRPC和Protobuf介绍,就不介绍了,网络上一搜一大把,随便一抓都是标准的官方,所以直接从使用说起。

  gPRC源代码:https://github.com/grpc/grpc;

  protobuf的代码仓库:github仓库地址:https://github.com/google/protobuf ;Google下载protobuff下载地址:https://developers.google.com/protocol-buffers/docs/downloads 。

1、新建解决方案

  分别在VS中新建解决方案:GrpcTest;再在解决方案中新建三个项目:GrpcClient、GrpcServer、GrpcService,对应的分别是客户端(wpf窗体程序)、服务端(控制台程序)、gRPC服务者(控制台程序)。在GrpcClient和GrpcServer项目中添加对GrpcService的引用。

  在VS中对3个项目添加工具包引用:右键点击“解决方案gRPCDemo”,点击“管理解决方案的NuGet程序包”,在浏览中分别搜索"Grpc"、"Grpc.Tools"、"Google.Protobuf",然后点击右面项目,全选,再点击安装(也可以用视图 -> 窗口 ->  程序包管理器控制台 中的"Install-Package Grpc"进行这一步,这里不提供这种方法,有兴趣自己百度)。

2、proto文件的语法

  对于使用gRPC的通信框架,需要使用到对应的通信文件。在gRPC中,使用到的是proto格式的文件,对应的自然有其相应的语法。本文不详细阐述该文件的语法,感兴趣可以去官网看标准的语法,这儿有一个链接,中文翻译比较全的https://www.codercto.com/a/45372.html。需要对其文章内的1.3进行补充下:

  • required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的。
  • optional:消息格式中该字段可以有0个或1个值(不超过1个)。
  • repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于C#中的List。

  本示例项目实现文件传输,因此在项目GrpcService中添加一个FileTransfer.proto文件,文件内容如下:

复制代码
syntax = "proto3";
package GrpcService;

service FileTransfer{
    rpc FileDownload (FileRequest) returns (stream FileReply);
    rpc FileUpload (stream FileReply) returns(stream FileReturn);
}

//请求下载文件时,所需下载文件的文件名称集合
message FileRequest{
    repeated string FileNames=1;//文件名集合
    //repeated重复字段  类似链表;optional可有可无的字段;required必要设置字段
    string Mark = 2;//携带的包
}

//下载和上传文件时的应答数据
message FileReply{
    string FileName=1;//文件名
    int32 Block = 2;//标记---第几个数据
    bytes Content = 3;//数据
    string Mark = 4;//携带的包
 }

//数据上传时的返回值
message FileReturn{
    string FileName=1;//文件名
    string Mark = 2;//携带的包
}
复制代码

3、编译proto文件为C#代码

  proto文件仅仅只是定义了相关的数据,如果需要在代码中使用该格式,就需要将它编译成C#代码文件。

    PS:网上可以找到的编译,需要下载相关的代码,见博文。其他的也较为繁琐,所以按照自己理解的来写了。注意,我的项目是放在D盘根目录下的。

  首先打开cmd窗口,然后在窗口中输入:D:\GrpcTest\packages\Grpc.Tools.2.32.0\tools\windows_x86\protoc.exe -ID:\GrpcTest\GrpcService --csharp_out D:\GrpcTest\GrpcService D:\GrpcTest\GrpcService\FileTransfer.proto --grpc_out D:\GrpcTest\GrpcService --plugin=protoc-gen-grpc=D:\GrpcTest\packages\Grpc.Tools.2.32.0\tools\windows_x86\grpc_csharp_plugin.exe

  输入上文后,按enter键,回车编译。

  命令解读:

  • D:\GrpcTest\packages\Grpc.Tools.2.32.0\tools\windows_x86\protoc.exe :调用的编译程序路径,注意版本不同路径稍有不一样。
  • -ID:\GrpcTest\GrpcService :-I 指定一个或者多个目录,用来搜索.proto文件的。所以上面那行的D:\GrpcTest\GrpcService\FileTransfer.proto 已经可以换成FileTransfer.proto了,因为-I已经指定了。注意:如果不指定,那就是当前目录。
  •  --csharp_out D:\GrpcTest\GrpcService D:\GrpcTest\GrpcService\FileTransfer.proto :(--csharp_out)生成C#代码、存放路径、文件。当然还能cpp_out、java_out、javanano_out、js_out、objc_out、php_out、python_out、ruby_out 这时候你就应该知道,可以支持多语言的,才用的,生成一些文件,然后给各个语言平台调用。参数1(D:\GrpcTest\GrpcService)是输出路径,参数2(D:\GrpcTest\GrpcService\FileTransfer.proto)是proto的文件名或者路径。
  •  --grpc_out D:\GrpcTest\GrpcService :grpc_out是跟服务相关,创建,调用,绑定,实现相关。生成的玩意叫xxxGrpc.cs。与前面的区别是csharp_out是输出类似于咱们平时写的实体类,接口,定义之类的。生成的文件叫xxx.cs
  • --plugin=protoc-gen-grpc=D:\GrpcTest\packages\Grpc.Tools.2.32.0\tools\windows_x86\grpc_csharp_plugin.exe :这个就是csharp的插件,python有python的,java有java的。

  编译后,会在新增两个文件(文件位置与你的输出位置有关),并将两个文件加入到GrpcService项目中去:

    

 

 4、编写服务端的文件传输服务

  在GrpcServer项目中,新建一个FileImpl并继承自GrpcService.FileTransfer.FileTransferBase,然后复写其方法FileDownload和FileUpload方法,以供客户端进行调用。

 View Code

  在main函数中添加服务:

复制代码
class Program
{
    static void Main(string[] args)
    {
        //提供服务
        Server server = new Server()
        {
            Services = {GrpcService.FileTransfer.BindService(new FileImpl())},
            Ports = {new ServerPort("127.0.0.1",50000,ServerCredentials.Insecure)}
        };
        //服务开始
        server.Start();

        while(Console.ReadLine().Trim().ToLower()!="exit")
        {

        }
        //结束服务
        server.ShutdownAsync();
    }
}
复制代码

5、编写客户端的文件传输功能

  首先定义一个文件传输结果类TransferResult<T>,用于存放文件的传输结果。

复制代码
/// <summary>
/// 传输结果
/// </summary>
/// <typeparam name="T"></typeparam>
class TransferResult<T>
{
    /// <summary>
    /// 传输是否成功
    /// </summary>
    public bool IsSuccessful { get; set; }
    /// <summary>
    /// 消息
    /// </summary>
    public string Message { get; set; }

    /// <summary>
    /// 标记类型
    /// </summary>
    public T Tag { get; set; } = default;
}
复制代码

  然后在GrpcClinet项目中添加一个FileTransfer的类,并实现相关方法:

 View Code

  现在可以在客户端窗体内进行调用了:

复制代码
private string GetFilePath()
{
    // Create OpenFileDialog 
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();

    // Set filter for file extension and default file extension 
    dlg.Title = "选择文件";
    dlg.Filter = "所有文件(*.*)|*.*";
    dlg.FileName = "选择文件夹.";
    dlg.FilterIndex = 1;
    dlg.ValidateNames = false;
    dlg.CheckFileExists = false;
    dlg.CheckPathExists = true;
    dlg.Multiselect = false;//允许同时选择多个文件 

    // Display OpenFileDialog by calling ShowDialog method 
    Nullable<bool> result = dlg.ShowDialog();

    // Get the selected file name and display in a TextBox 
    if (result == true)
    {
        // Open document 
        return dlg.FileName;
    }

    return string.Empty;
}
// 打开文件
private void btnOpenUpload_Click(object sender, RoutedEventArgs e)
{
    lblUploadPath.Content = GetFilePath();
}
CancellationTokenSource uploadTokenSource;
//上传
private async void btnUpload_Click(object sender, RoutedEventArgs e)
{
    lblMessage.Content = string.Empty;

    uploadTokenSource = new CancellationTokenSource();
    List<string> fileNames = new List<string>();
    fileNames.Add(lblUploadPath.Content.ToString());
    var result = await ServerNet.FileTransfer.FileUpload(fileNames, "123", uploadTokenSource.Token);

    lblMessage.Content = result.Message;

    uploadTokenSource = null;
}
//取消上传
private void btnCancelUpload_Click(object sender, RoutedEventArgs e)
{
    uploadTokenSource?.Cancel();
}


//打开需要下载的文件
private void btnOpenDownload_Click(object sender, RoutedEventArgs e)
{
    txtDownloadPath.Text = GetFilePath();
}
//下载文件
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
    lblMessage.Content = string.Empty;

    downloadTokenSource = new CancellationTokenSource();
    List<string> fileNames = new List<string>();
    fileNames.Add(System.IO.Path.GetFileName(txtDownloadPath.Text));
    var result=  await ServerNet.FileTransfer.FileDownload(fileNames, "123", Environment.CurrentDirectory, downloadTokenSource.Token);

    lblMessage.Content = result.Message;

    downloadTokenSource = null;
}
CancellationTokenSource   downloadTokenSource;
//下载取消
private void btnCancelDownload_Click(object sender, RoutedEventArgs e)
{
    downloadTokenSource?.Cancel();
}
复制代码

6、源代码

  https://files.cnblogs.com/files/pilgrim/GrpcTest.rar

I travel alone,not to be alone.
 
出处:https://www.cnblogs.com/pilgrim/p/13855661.html

相关教程