サイトアイコン Unity+AssetStoreおすすめ情報

C# で gRPC を使ってみた、ついでに Stream でファイル転送

はじめに

gRPC-Web が正式リリース されたとの事を聞いて、やっぱり gRPC が今後の通信プロトコルのデファクトスタンダードになるのかなと思い実際に使って使用感を確かめてみました。
gRPC-Web ではなく gRPC になります

開発環境

学んだ内容

プログラム実装

チュートリアルの実行

gRPCの公式ドキュメントを見なかった理由はとりあえず動かしてみたかったからです。
codelabs の gRPC(C#) のチュートリアルを実行してみました。

Streamを使ってみた(ファイル転送)

greeter.proto ファイルに定義を追加

greeter.proto

message Empty {
}

message Chunk {
    bytes chunk = 1;
}

service GreetingService {
    rpc greeting(HelloRequest) returns (HelloResponse);
    rpc filesend(stream Chunk) returns (Empty);
}

バッチで通信プログラムを生成

generate_protos.bat を実行

サーバのソースコード

filesend の受信時のメソッドを追加

GreeterServiceImpl.cs

namespace GreeterServer
{
    public class GreeterServiceImpl : GreetingService.GreetingServiceBase
    {
        public override Task<HelloResponse> greeting(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloResponse { Greeting = "Hello " + request.Name });
        }

        public override async Task<Empty> filesend(IAsyncStreamReader<Chunk> stream, ServerCallContext context)
        {
            List<byte> bytes = new List<byte>();
            await stream.ForEachAsync(request =>
            {
                var temp = request.Chunk_.ToByteArray();
                bytes.AddRange(temp);
                return Task.CompletedTask;
            });

            Console.WriteLine($"size={bytes.Count}");
            // Console.WriteLine(BitConverter.ToString(bytes.ToArray()));

            // 受信完了を返す
            return new Empty();
        }
    }
}

クライアントのソースコード

ファイルの読み込みを行い、送信処理を100回送ります。

4MBに収まるファイルでのみ動作確認しています(超える場合は、4MBに分割してリクエストメッセージを複数投げたら動くと思います)

Program.cs

namespace GreeterClient
{
    class Program
    {
        const string Host = "localhost";
        const int Port = 50051;

        public static void Main(string[] args)
        {
            // Create a channel
            var channel = new Channel(Host + ":" + Port, ChannelCredentials.Insecure);

            // Create a client with the channel
            var client = new GreetingService.GreetingServiceClient(channel);

            // Create a request
            var request = new HelloRequest{
                Name = "Mete - on C#",
                Age = 34,
                Sentiment = Sentiment.Happy
            };

            // Send the request
            Console.WriteLine("GreeterClient sending request");
            var response = client.greeting(request);
            Console.WriteLine("GreeterClient received response: " + response.Greeting);

            // チャンクデータの作成
            var fs = new FileStream("00013646_72B.jpg", FileMode.Open);
            byte[] data = new byte[fs.Length];
            fs.Read(data, 0, data.Length);

            // ファイルの送信処理
            var task = Task.Run(async () =>
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < 100; i++)
                {
                    // 送信
                    var stream = client.filesend();
                    var chunk = Google.Protobuf.ByteString.CopyFrom(data, 0, data.Length);
                    await stream.RequestStream.WriteAsync(new Chunk() { Chunk_ = chunk });
                    await stream.RequestStream.CompleteAsync();
                    var res = await stream.ResponseAsync;
                }
                sw.Stop();
                Console.WriteLine(sw.Elapsed);
            });
            task.Wait();

            // Shutdown
            channel.ShutdownAsync().Wait();
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

実行結果

まとめ

めっちゃ簡単なのでこれからも使っていきたい。
非同期での処理が前提なので C# 以外の言語でどう記述するのかいまいちわかっていないがこれから使う機会があれば勉強できるので今のところはここまでにします。

モバイルバージョンを終了