Edited at

UnityでgRPCを動かす

More than 1 year has passed since last update.


準備


前提

Unity + gRPCでは、MagicOnionというライブラリが有名ですが、ここではそれを使いません。

ただし、MagicOnionの修正内容は参考にしました...


以下をUnityプロジェクトに入れる


  • gRPCのC#のソースコード

    grpc/src/csharp/Grpc.Core

  • protobufのC#のソースコード

    protobuf/csharp/src/Google.Protobuf


エラーを粛々と取り除く


IAsyncEnumeratorが無いと怒られる

Assets/Grpc.Core/IAsyncStreamReader.cs(53,46): error CS0246: The type or namespace name `IAsyncEnumerator' could not be found. Are you missing an assembly reference?

IAsyncEnumeratorというインターフェースが無く、コンパイルエラーが出ますので、以下のような定義を追加します。

public interface IAsyncEnumerator<T> : IDisposable

{
T Current { get; }
Task<bool> MoveNext(CancellationToken token);
}


MoveNextの引数がないと言われる

Assets/Grpc.Core/Internal/ServerCallHandler.cs(69,69): error CS1501: No overload for method `MoveNext' takes `0' arguments

これは、Grpc.CoreがRx.NETに依存しているためこのようなエラーが表示されます。

なので、さらに以下のコードを追加します(namespaceやファイルの配置場所はそれぞれの事情に合わせてやってね)

    public static class AsyncEnumerator

{
public static Task<bool> MoveNext<T>(this IAsyncEnumerator<T> enumerator)
{
if (enumerator == null)
throw new ArgumentNullException(nameof(enumerator));

return enumerator.MoveNext(CancellationToken.None);
}
}

これは、Rx.NETのコードの中身そのままです。

Rx.NETまでUnityに入れようか悩みましたが、UniRxの競合とかとも考えられるためこの程度にしています。


この時点でコンパイルが通る

エラーが出なくなりました。では、次に実行してみましょう。


デバッグ実行用のSSLの証明書がないと言われる

実行するとこのエラーが出ました



IOException: Error loading the embedded resource "Grpc.Core.roots.pem"



SSLの証明書がない場合のエラーと考察し、どうしようかと考えていましたがここで、MagicOnion の修正方法を参考にさせていただきました。DefaultSslRootsOverride.cs を以下のように書き換えます。まあ、中身はまるっとその処理を無視するような内容です。


using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;

namespace Grpc.Core.Internal
{
/// <summary>
/// Overrides the content of default SSL roots.
/// </summary>
internal static class DefaultSslRootsOverride
{
//const string RootsPemResourceName = "Grpc.Core.roots.pem";
static object staticLock = new object();

/// <summary>
/// Overrides C core's default roots with roots.pem loaded as embedded resource.
/// </summary>
public static void Override(NativeMethods native)
{
lock (staticLock)
{
//var stream = typeof(DefaultSslRootsOverride).GetTypeInfo().Assembly.GetManifestResourceStream(RootsPemResourceName);
//if (stream == null)
//{
// throw new IOException(string.Format("Error loading the embedded resource \"{0}\"", RootsPemResourceName));
//}
//using (var streamReader = new StreamReader(stream))
//{
// var pemRootCerts = streamReader.ReadToEnd();
// native.grpcsharp_override_default_ssl_roots(pemRootCerts);
//}
}
}
}
}

これはMagicOnionのソースコードそのままです。

これで、この実行時のエラーは出なくなります。


DllNotFoundException: grpc_csharp_ext

grpc_charp_exが無いとエラーが表示されます。



DllNotFoundException: grpc_csharp_ext

Grpc.Core.Internal.NativeLogRedirector.Redirect (Grpc.Core.Internal.NativeMethods native) (at Assets/Grpc.Core/Internal/NativeLogRedirector.cs:49)



なので、それを入手してDLLをAssets配下に置く必要があるのですが、ここで自分は手間取りました。

Ubuntsu on Windowsを使っていたせいなのかはわからないですが、grpc本家のREADMEを読みつつビルドを試しましたが、うまくいかず、結局サンプルコードをVisual Studioでビルドし、NuGetパッケージの中かからdllを抜き出すということをしました


参考


なんだかんだあって、grpc_csharp_ext.dllをUnityプロジェクトに入れる

ここで、grpcのレポジトリ内にあるサンプルプロジェクトをビルドし、サーバーを立ち上げます。

https://github.com/grpc/grpc/tree/master/examples/csharp/helloworld

この中身をVisual Studioでビルドするだけでサーバー側のプログラムまでビルドされるので、ビルドしたサーバー側のexeファイルを立ち上げます。Windows以外の環境であれば、READMEの手順の通りに .NET Core インストールしつつビルドすることで実行できるかと思います。


クライアント側のサンプルコードを参考にしつつUnity側のプログラムを書く

まず、gRPC自体のサンプルプロジェクトには .proto と言われるサーバー側との通信規約をもとにしたコードもあるので、それをUnityプロジェクトに入れます。

3bdce428a37fe5aba1e59e6acdbf54b8.png

ここにある HelloWorld.cs/HelloWorldGRPC.cs がそれです。

これは、gRPCのコード生成プログラムによって吐かれるコードですがサンプルプロジェクトには含まれています。


UnityからCallする処理を入れる

以下のようなコードを書きます


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Grpc.Core;
using Helloworld;

public class TestGrpc : MonoBehaviour
{
void Start ()
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);

var client = new Greeter.GreeterClient(channel);
String user = "Test UserName";

var reply = client.SayHello(new HelloRequest { Name = user });
Debug.Log("Greeting: " + reply.Message);
}
}

これは、gRPCのC#サンプルプロジェクトの中身をそのままUnityのStartに入れ込んだだけです。

適当なGameObjectにこれをアタッチして実行します。

サーバー側のプログラムを立ち上げた上で実行すると、以下のようにちゃんとレスポンスを受け取れました!

d77a882b1dc6e1bce7cc29c7ad487ddf.png


その他

Android/iOSで動かすには、grpc_csharp_ext.dll をそれぞれのプラットフォームごとコンパイルするという苦行が待ってます。

https://github.com/grpc/grpc/blob/master/src/csharp/experimental/README.md

こちらにそのやり方は書かれてますが、まだiOSはTBD扱いです。。。

UniRxの作者の方が、いろいろやり取りされているので、できるんだと思います。

(grpc_csharp_extの中身はCのコードなのでXCodeでゴニョゴニョするとできるはず)

次は各プラットフォームの対応 + リアルタイムチャットなどをgRPCで出来たらなと思います。