logo

GO言語サーバーとUnityクライアント(C#)をgRPCで接続テストしてみた【入門レベル】

投稿日2022-04-05

更新日2024-05-17

Picture of the logo
目次(タップして移動)

UnityでgRPCをする場合、Magic Onionを使うのがお手軽ですが、今回はGO言語でサーバーを構築してみたかったので最低限の接続機能を自作してみました。備忘録を兼ね書き残しておきます。

テスト環境

実際にテストに使用した環境を紹介します。このバージョンでないと動かないという訳ではありません。

サーバー側

  • localhost(windows10)
  • go version go1.18

クライアント側

  • windows10
  • Unity 2020.3.26f1
  • gRPC_UNITYパッケージ 2.46

サーバー側構築

GO言語をすでにインストール済みであるものとします。

ディレクトリ構造

grpc-go(モジュール名)
├─cmd(main.goの置き場所)
├─pb(protocから生成されたファイル)
├─pkg
│  └─service(呼び出されるサービス定義)
└─proto(.protoファイルの置き場所)

.protoファイルの作成

syntax="proto3";

package grpc;

option go_package = "../pb";

message Request {
    string text=1;
    }

message Response {
    string text=1;
    }

service Test{
    rpc GrpcTest(Request) returns (Response) {}
}

上記のような最低限の内容でgrpc.protoというファイルを作りprotoディレクトリに保存する。(ファイルやディレクトリ名は任意です) 文字列を何か送って何か文字列を返す、ただそれだけの関数です。

.protoファイルのコンパイル

.protoファイルが出来たら、go言語用(サーバー用)とc#用(unityクライアント用)それぞれにコンパイルする必要があります。コンパイルにはprotocというコマンドを使います。

プラグインのインストール

コンパイルには各プログラム言語に対応したプラグインが必要なので先にダウンロードする。各言語のプラグインは自分でインストールしないといけないようです。

  • go言語 こちらのサイトを参考にgo install ~コマンドでインストールして下さい。
  • go言語以外の言語 こちらのサイトからダウンロード出来ます。build idのリンクをクリックして表示されたサイトのPluginsの中から必要なものをダウンロードして下さい。

gRPCプラグイン

go用

.protoファイルと同じディレクトリで下記コマンドを実行すると、ファイルが2つできるので、それをpbディレクトリに移動します。

protoc --go_out=. --go_opt=paths=source_relative     --go-grpc_out=. --go-grpc_opt=paths=source_relative  grpc.proto

c#用

上記サイトでプラグインをダウンロードする。 .tar.gz形式で圧縮されているので各自のOSの方法で解凍して下さい。 解凍するとgrpc_cpp_pluginのような名前のファイルが7つくらい出来るのでprotocが使用するディレクトリにコピーする。(linuxの場合は/usr/local/bin/等)

protoc -I ./proto --csharp_out=. --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_csharp_plugin grpc.proto

出力された2つのファイルは後でunityプロジェクトの中にコピーします。

サーバー用プログラムを作成する

作成したcmdディレクトリにmain.goとでも名付けて立ち上げ用プログラムを作ります。今回サービス部分はファイルを分けてimportするようにしています。

package main

import (
    "fmt"
    "log"
    "net"

pb "grpc-go/pb"
service "grpc-go/pkg/service"

"google.golang.org/grpc"
)

func main() {

lis, err := net.Listen("tcp", ":8080")
if err != nil {
    log.Fatalf("failed to listen: %v", err)
    }

    grpcServer := grpc.NewServer()

    pb.RegisterTestServer(grpcServer, service.NewTestServer())

    fmt.Println("Now listening...")
    if err := grpcServer.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %s", err)
    }
}

pkg/serviceというディレクトリを作成し、その中にサービスの中身になるプログラムを書きます。今回は開通テストなので非常にシンプルなものです。

package service

import (
    "context"
    pb "grpc-go/pb"
    "log"
)

type server struct {
    pb.UnimplementedTestServer
}

func (*server) GrpcTest(ctx context.Context, in *pb.Request) (*pb.Response, error) {
    // Getxxxメソッドも自動に作成されています。
    result := in.Text + " を受け取ったよ!"
    log.Printf("Received " + in.Text)
    // 受け取ったメッセージを連結したレスポンスを返します。
    return &pb.Response{
        Text: result,
    }, nil
}

//初期化用メソッド
func NewTestServer() *server {
    return &server{}
}

Unityクライアント側

gRPCモジュールをUnityプロジェクトにコピーする

前述のgRPCモジュールサイトのbuild idリンクをクリックして表示されたサイトの中からc# grpc_unity_packageのzipファイルをダウンロードして解凍して下さい。中にpluginsというディレクトリがあるので、そのディレクトリごとunityプロジェクトのAssets配下にコピーして下さい。

unityプラグイン

protocの生成コードをコピーする

scriptsディレクトリ配下にgeneratedとでも名付けたディレクトリを作り、先ほどprotocで作成したgrpc.pb.csとgrpcgrpc.pb.csをコピーする

呼び出し用スクリプト作成

下記のようなスクリプトを書いて空オブジェクトにアタッチする。サーバーに接続してテスト用の文字列を送信して返事が返ってきたら成功。ただそれだけのスクリプトです。

using UnityEngine;
using Grpc.Core;
using System.Threading;

public class controller : MonoBehaviour
{
    private Channel channel;
    private CancellationToken ct;
    void Start()
    {
        ct = new CancellationToken();

        CallTest();
    }
    public async void CallTest()
    {
        string host = "localhost";
        int port = 8080;

        // channel = new Channel(host, port, new SslCredentials(cacert, keyCertificatePair));   //ssl化してたときの名残
        channel = new Channel(host, port, ChannelCredentials.Insecure);

        var client = new Grpc.Test.TestClient(channel);
        
        string request = "unityから送られる文字列";
        
        var response = await client.GrpcTestAsync(new Grpc.Request { Text = request }, null, null, ct);
        var result = response.Text;

        Debug.Log($"Result: {result}");
    }
    async void OnDestroy()
    {
        await this.channel.ShutdownAsync();
    }
}

実際に接続してみる

go run cmd/main.goと起動してlisten状態になったらunity側を実行します。

実行結果

サーバー側にはこのようなログが表示され

サーバーログ

unity側にはこのようなログが返ってくれば、接続は成功です。

unityログ

接続まで出来たら、あとは色々な関数を作って遊んでみて下さい。






このサイトをシェアする