読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

Protocol Buffersを利用してC#とJava間でデータ連携するサンプル (protobuf-net, protoc, protogen)


忘れない内にメモメモ。


Protocol Buffersの説明自体はWikipediaさんなどに分かりやすく記述されています。


本家のProtocol Buffersでは、C++JavaPythonに対応しています。
C#から利用する場合は、protobuf-netを利用します。


やり方としては

  • protoファイルの作成
  • JavaファイルとCSファイルを出力 (java側はprotoc, C#側はprotogen)
  • 出力されたソースを利用して処理を行う.

となります。


で、まず最初にprotoファイルを作成する必要があります。
protoファイルの書き方については、以下に記載があります。


今回は以下のようなシンプルなprotoファイルを作成
名前はtest.protoとしました。

message HelloWorld {
  required string message = 1;
  required int32 age = 2;
  required bytes bytearray = 3;
}

見たまんまですが、文字列フィールド、数値フィールド、バイト配列フィールドとなります。


これをprotobufが用意してくれているコンパイラに食わせます。
これによって、JavaとCSのソースファイルが出力されます。
ただし、C#の場合は、通常のprotocを利用するのではなく、protobuf-netのprotogenを利用します。


まず、Javaソースファイルを出力.
コマンドラインで以下のようにします。

protoc --java_out=出力ディレクトリ protoファイル


次に、C#ソースファイルを出力.
コマンドラインで以下のようにします。

protogen -i:protoファイル -o:csファイル名


これで準備完了です。出力されたソースファイルを適切な場所に配置します。
後はJava側とC#側に必要なライブラリを参照設定して利用します。


JavaMaven利用している場合は以下の依存関係をpom.xmlに記述。

    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>2.5.0</version>
    </dependency>


C#側では、nuget利用してprotobuf-netをインストールしたりすると楽です。
protobuf-netをmsiインストーラとかでインストールしている場合は
インストールディレクトリの下にdllファイルが存在しますので、それを参照設定してもオッケイです。


で、後は処理を記述すればオッケイです。
今回はJava側がサーバでC#側がクライアントと仮想定して
ソケットで通信してデータをやり取りしてます。


まずは、java側。
たいした事してませんが、一応サンプルです。
サーバー起動して後は待ってるだけです。

package protobufsample;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

import protobufsample.Test.HelloWorld;

public class App {
  public static void main(String[] args) throws Exception {
    App me = new App();
    me.start();
  }

  public void start() throws IOException {

    try (ServerSocket server = new ServerSocket(9999)) {

      System.out.println("server started...");

      try (Socket client = server.accept()) {

        System.out.println("got client connection...");

        try (InputStream is = client.getInputStream()) {

          while (is.available() == 0) {
            //
            // 利用可能になるまで待機
            //
          }

          byte[] bytes = new byte[is.available()];
          is.read(bytes);

          HelloWorld obj = HelloWorld.parseFrom(bytes);

          System.out.printf("%s, %d, %s\n", obj.getMessage(), obj.getAge(), obj.getBytearray().toString());
        }
      }
    }
  }
}


次はC#側。
こちらはデータを作成して、サーバーに送信しています。

namespace ProtoBufTest
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.IO;
  using System.Net;
  using System.Net.Sockets;

  using ProtoBuf;

  class Program
  {
    static void Main(string[] args)
    {
      var obj = new test.HelloWorld();
      obj.message = "hello protobuf";
      obj.age = 99;
      obj.bytearray = new byte[] { 20, 21, 22 };

      using (var client = new TcpClient())
      {
        client.Connect(new IPEndPoint(IPAddress.Loopback, 9999));
        using (var stream = client.GetStream())
        {
          Serializer.Serialize<test.HelloWorld>(stream, obj);
        }
      }

      Console.WriteLine("data sended....");
      Console.ReadLine();
    }
  }
}


余談ですが、Java側でデータを作成する場合は以下のようにします。

    public static void main(String[] args) throws Exception
    {
      byte[] bytes = new byte[3];
      bytes[0] = 20;
      bytes[1] = 21;
      bytes[2] = 22;
      
      HelloWorld hw = HelloWorld
                        .newBuilder()
                        .setMessage("Hello World Protobuf")
                        .setAge(99)
                        .setBytearray(ByteString.copyFrom(bytes))
                        .build();
    }

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。