おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

gRPCのBidirectional streaming通信をC#で体験してみた。

はじめに

最近少しずつ名前を聞くようになってきたgRPCを学習してみました。

gRPCには4つの通信方法がありますが、今回はそのうちの1つであるBidirectional streaming通信をC#で体験してみようと思います。

gRPCについて

Unary通信を体験したときに引用した2記事がとても分かりやすかったです!

gRPCのUnary通信をC#で体験してみた。 - おーみんブログ

作るもの

今回作るものはコンソールアプリ(クライアント側)で任意のメッセージをサーバ側へ送信したり、サーバから適当なメッセージが送られてくるような内容を作りたいと思います。
(イメージとしてはチャットルーム的な感じです)

protoファイル

まずはprotoファイルを作ります。

syntax = "proto3";

package DummyChat;

service DummyChatManager {
  rpc DummyChatManage (stream ChatRequest) returns (stream ChatResponse);
}

message ChatRequest {
  string message = 1;
}

message ChatResponse {
  string message = 1;
}

Serviceで定義しているメソッドの引数, 返却値両方にstreamと付けてあげるのがポイントです。

サーバ側の実装

protoファイルから自動生成されたコードの中に{Service名} + "Base"のクラスがあるので、そちらを継承してサービスの処理を作ります。

今回はチャットアプリをイメージしているので、ちょ~仮という感じではありますが、簡単な固定メッセージをクライアント側へ送るものとします。

public sealed class DummyChatService : DummyChatManager.DummyChatManagerBase
{
    public override async Task DummyChatManage(IAsyncStreamReader<ChatRequest> requestStream, IServerStreamWriter<ChatResponse> responseStream, ServerCallContext context)
    {
        // クライアントからのメッセージ処理
        var readTask = Task.Run(async () =>
        {
            await foreach (var message in requestStream.ReadAllAsync())
            {
                Console.WriteLine(message.Message);
            }
        });

        // クライアント側からやり取り完了の合図があるまではメッセージを送るものとする
        while (!readTask.IsCompleted)
        {
            await Task.Delay(TimeSpan.FromSeconds(10), context.CancellationToken);
            await responseStream.WriteAsync(new ChatResponse { Message = "---- Aさんが入室しました! ----" });
            await Task.Delay(TimeSpan.FromSeconds(10), context.CancellationToken);
            await responseStream.WriteAsync(new ChatResponse { Message = "A: 忙しいので抜けます~!" });
            await Task.Delay(TimeSpan.FromSeconds(10), context.CancellationToken);
            await responseStream.WriteAsync(new ChatResponse { Message = "---- Aさんが退室しました...。---- " });
        }
    }
}

クライアント側の実装

using DummyChat;
using Grpc.Core;
using Grpc.Net.Client;

// サーバのURLを適宜変更する必要があります。
using var channel = GrpcChannel.ForAddress("https://localhost:7015");
var client = new DummyChatManager.DummyChatManagerClient(channel);
var stream = client.DummyChatManage();

// 相手からのメッセージは逐一受け取る
var responseReadTask = Task.Run(async () => 
{
    await foreach(var response in stream.ResponseStream.ReadAllAsync())
    {
        Console.WriteLine(response.Message);
    }
});

// 送信するメッセージ
var message = string.Empty;

// exitと入力すると自身が退室扱いとなる。
while(message != "exit")
{
    message = Console.ReadLine();
    await stream.RequestStream.WriteAsync(new ChatRequest { Message = message });
}

// サーバへメッセージの送信が完了した旨を通知
await stream.RequestStream.CompleteAsync();

await responseReadTask;

Console.WriteLine("Press any key to exit...");
Console.ReadKey();

実行してみる

サーバを立ち上げた状態でクライアントアプリ(コンソールアプリ)を実行するとメッセージが送られてきているのが分かります。
(チャット風に見せるようにこちらからのメッセージはタイミングよく送っています。)

こんにちは!誰かいますか?
---- Aさんが入室しました! ----
Aさん!よろしくです!
A: 忙しいので抜けます~!
あれ?なら僕も。。。
exit
---- Aさんが退室しました...。----
Press any key to exit...

上記のAさん関連のメッセージがサーバから送られてきている内容で、それ以外が僕の送った内容です。

おわりに

以上、gRPCのBidirectional streaming通信をC#で体験してみたという内容でした。 こちらは主にチャットアプリなどを作成する際に用いられるみたいですね。

補足

その他の通信についても以下の記事で体験しているのでご興味があればぜひ!

gRPCのUnary通信をC#で体験してみた。 - おーみんブログ

gRPCのServer Streaming通信をC#で体験してみた。 - おーみんブログ

gRPCのClient Streaming通信をC#で体験してみた。 - おーみんブログ