Introduction

.NETでRPCを試してみる gRPC編 第0回のように、最新のRPCとしてgRPCを試しました。
さまざまな言語をサポートしているので、C++でしか動かない (C#のP/InvokeやC++/CLIからも動作しない) ライブラリをRPCで動かそうと思いました。
ところが、C++でgRPCを動かすのは面倒な模様。

  • CMake
  • Active State Perl
  • Ninja
  • Go
  • yasm

といったモジュールのインストールが必要のため断念。

かわりに見つけたのがApache Thrift
こちらも、クライアント/サーバーのコードを生成して利用するため、ロジックの実装に注力できるRPCフレームワーク。
公式は下記。

今回は、クライアントをC#、サーバーをC++として試してみます。

Preparation

Thriftのライブラリは自分でビルドする必要があります。
そのために、下記の手順で、関連ライブラリなどを入手していきます。

boost

Boost C++ Librariesからインストーラを落としてきて展開します。
2017/04/09時点で、1.63が安定版の最新です。

OpenSSL

Heartbleedでも有名になったSSLのライブラリですね。公式ページは下記。

OpenSSLは、2017/04/09時点で、1.1.0eが安定版の最新ですが、1.1.0系はネット上で転がっている情報が少ないし、ビルド方法が違っていて、ビルドできなかったので、1.0.2系を使います。
2017/04/09時点で、1.0.2jが1.0.2系の最新です。

一応説明すると、バイナリの入手は2通りあります。

  • 有志が配布しているバイナリの入手
    • 現在有志が配布していて、最新版はインストーラのみで環境が汚れてしまう、かつ寄付を求められます。
  • 自分でビルド
    • Perlのインストールが必要で、環境が汚れてしまう。
    • Perlの有名どころのActivePerlは無償と有償がある。企業利用でも、pre-production development and prototyping なら無償版が使える。
      * ビルドのためだけなので無償版でOK?
    • 最近は、Strawberry Perlというのがあり、かつzipのようなポータブル版もある。

というわけで、インストーラは嫌なので、今回は自分でビルドするために、Strawberry PerlのPortable版を入手しました。
2017/04/09時点で、Strawberry Perlは、5.24.1.1が安定版の最新です。

Portable版でも123MBあります。展開後は、400MBを超えますのでディスク容量には注意。
Strawberry Perlを展開後、スタート > 全てのプログラム > Visual Studio 2015 > Windows Desktop Command Prompts > VS2015 x64 Native Tools コマンド プロンプトを起動、そしてStrawberry Perlの展開先まで移動し、下記のように実行します。

1
2
3
4
5
6
7
8
D:\Works\Lib\Strawberry Perl\5.24.1.1>portableshell.bat
----------------------------------------------
Welcome to Strawberry Perl Portable Edition!
* URL - http://www.strawberryperl.com/
* see README.TXT for more info
----------------------------------------------
Perl executable: D:\Works\Lib\Strawberry Perl\5.24.1.1\perl\bin\perl.exe
Perl version : 5.24.1 / MSWin32-x64-multi-thread

続いて、OpenSSLの展開先まで移動します。そして、下記のように実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
c:\openssl-1.0.2j>mkdir "openssl-1.0.2j_x64_VC-WIN"
c:\openssl-1.0.2j>set TARGET=%CD%\openssl-1.0.2j_x64_VC-WIN
c:\openssl-1.0.2j>perl Configure VC-WIN64A --prefix="%TARGET%"
no-asm
Configuring for VC-WIN64A
no-asm [option] OPENSSL_NO_ASM
no-ec_nistp_64_gcc_128 [default] OPENSSL_NO_EC_NISTP_64_GCC_128 (skip dir)
no-gmp [default] OPENSSL_NO_GMP (skip dir)
no-jpake [experimental] OPENSSL_NO_JPAKE (skip dir)
no-krb5 [krb5-flavor not specified] OPENSSL_NO_KRB5
no-libunbound [experimental] OPENSSL_NO_LIBUNBOUND (skip dir)
no-md2 [default] OPENSSL_NO_MD2 (skip dir)
no-rc5 [default] OPENSSL_NO_RC5 (skip dir)
no-rfc3779 [default] OPENSSL_NO_RFC3779 (skip dir)
no-sctp [default] OPENSSL_NO_SCTP (skip dir)
no-shared [default]
no-ssl-trace [default] OPENSSL_NO_SSL_TRACE (skip dir)
no-ssl2 [default] OPENSSL_NO_SSL2 (skip dir)
no-store [experimental] OPENSSL_NO_STORE (skip dir)
no-unit-test [default] OPENSSL_NO_UNIT_TEST (skip dir)
no-weak-ssl-ciphers [default] OPENSSL_NO_WEAK_SSL_CIPHERS (skip dir)
no-zlib [default]
no-zlib-dynamic [default]
IsMK1MF=1
CC =cl
CFLAG =-DOPENSSL_THREADS -DDSO_WIN32 -W3 -Gs0 -Gy -nologo -DOPENSSL_SYS
NAME_WIN32 -DWIN32_LEAN_AND_MEAN -DL_ENDIAN -DUNICODE -D_UNICODE -D_CRT_SECURE_NO_DEPRECATE
EX_LIBS =
CPUID_OBJ =mem_clr.o
BN_ASM =bn_asm.o
EC_ASM =
DES_ENC =des_enc.o fcrypt_b.o
AES_ENC =aes_core.o aes_cbc.o
BF_ENC =bf_enc.o
CAST_ENC =c_enc.o
RC4_ENC =rc4_enc.o rc4_skey.o
RC5_ENC =rc5_enc.o
MD5_OBJ_ASM =
SHA1_OBJ_ASM =
RMD160_OBJ_ASM=
CMLL_ENC =camellia.o cmll_misc.o cmll_cbc.o
MODES_OBJ =
ENGINES_OBJ =
PROCESSOR =
RANLIB =true
ARFLAGS =
PERL =perl
SIXTY_FOUR_BIT mode
DES_INT used
RC4_CHUNK is unsigned long long

Configured for VC-WIN64A.

次に、do_win64a.batというバッチを実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
C:\openssl-1.0.2j>ms\do_win64a.bat

C:\openssl-1.0.2j>perl util\mkfiles.pl 1>MINFO

C:\openssl-1.0.2j>cmd /c "nasm -f win64 -v" 1>NUL 2>&1

C:\openssl-1.0.2j>if 0 NEQ 0 goto ml64

C:\openssl-1.0.2j>perl ms\uplink-x86_64.pl nasm 1>ms\uptable.asm

C:\openssl-1.0.2j>nasm -f win64 -o ms\uptable.obj ms\uptable.asm
ms\uptable.asm:17: warning: absolute address can not be RIP-relative
ms\uptable.asm:24: warning: absolute address can not be RIP-relative
ms\uptable.asm:37: warning: absolute address can not be RIP-relative
ms\uptable.asm:44: warning: absolute address can not be RIP-relative
ms\uptable.asm:57: warning: absolute address can not be RIP-relative
ms\uptable.asm:64: warning: absolute address can not be RIP-relative
ms\uptable.asm:77: warning: absolute address can not be RIP-relative
ms\uptable.asm:84: warning: absolute address can not be RIP-relative
ms\uptable.asm:97: warning: absolute address can not be RIP-relative
ms\uptable.asm:104: warning: absolute address can not be RIP-relative
ms\uptable.asm:117: warning: absolute address can not be RIP-relative
ms\uptable.asm:124: warning: absolute address can not be RIP-relative
ms\uptable.asm:137: warning: absolute address can not be RIP-relative
ms\uptable.asm:144: warning: absolute address can not be RIP-relative
ms\uptable.asm:157: warning: absolute address can not be RIP-relative
ms\uptable.asm:164: warning: absolute address can not be RIP-relative
ms\uptable.asm:177: warning: absolute address can not be RIP-relative
ms\uptable.asm:184: warning: absolute address can not be RIP-relative
ms\uptable.asm:197: warning: absolute address can not be RIP-relative
ms\uptable.asm:204: warning: absolute address can not be RIP-relative
ms\uptable.asm:217: warning: absolute address can not be RIP-relative
ms\uptable.asm:224: warning: absolute address can not be RIP-relative
ms\uptable.asm:237: warning: absolute address can not be RIP-relative
ms\uptable.asm:244: warning: absolute address can not be RIP-relative
ms\uptable.asm:257: warning: absolute address can not be RIP-relative
ms\uptable.asm:264: warning: absolute address can not be RIP-relative
ms\uptable.asm:277: warning: absolute address can not be RIP-relative
ms\uptable.asm:284: warning: absolute address can not be RIP-relative
ms\uptable.asm:297: warning: absolute address can not be RIP-relative
ms\uptable.asm:304: warning: absolute address can not be RIP-relative
ms\uptable.asm:317: warning: absolute address can not be RIP-relative
ms\uptable.asm:324: warning: absolute address can not be RIP-relative
ms\uptable.asm:337: warning: absolute address can not be RIP-relative
ms\uptable.asm:344: warning: absolute address can not be RIP-relative
ms\uptable.asm:357: warning: absolute address can not be RIP-relative
ms\uptable.asm:364: warning: absolute address can not be RIP-relative
ms\uptable.asm:377: warning: absolute address can not be RIP-relative
ms\uptable.asm:384: warning: absolute address can not be RIP-relative
ms\uptable.asm:397: warning: absolute address can not be RIP-relative
ms\uptable.asm:404: warning: absolute address can not be RIP-relative
ms\uptable.asm:417: warning: absolute address can not be RIP-relative
ms\uptable.asm:424: warning: absolute address can not be RIP-relative
ms\uptable.asm:437: warning: absolute address can not be RIP-relative
ms\uptable.asm:444: warning: absolute address can not be RIP-relative

C:\openssl-1.0.2j>goto proceed

C:\openssl-1.0.2j>perl util\mk1mf.pl VC-WIN64A 1>ms\nt.mak

C:\openssl-1.0.2j>perl util\mk1mf.pl dll VC-WIN64A 1>ms\ntdll.mak

C:\openssl-1.0.2j>perl util\mkdef.pl 32 libeay 1>ms\libeay32.def

C:\openssl-1.0.2j>perl util\mkdef.pl 32 ssleay 1>ms\ssleay32.def

以上で、ビルドの準備ができました。
そして、下記のコマンドを実行することで、openssl-1.0.2j_x64_VC-WIN に成果物が出来上がります。

1
2
C:\openssl-1.0.2j>nmake -f ms\nt.mak
C:\openssl-1.0.2j>nmake -f ms\nt.mak install

成功すると、

  • openssl-1.0.2j_x64_VC-WIN\bin
  • openssl-1.0.2j_x64_VC-WIN\include
  • openssl-1.0.2j_x64_VC-WIN\lib
  • openssl-1.0.2j_x64_VC-WIN\ssl

というフォルダができています。

libevent

聞きなれないライブラリですが、公式ページ

によれば、

The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts. libevent is meant to replace the event loop found in event driven network servers

とのこと。つまり、特定のイベントが発生したりしたら、コールバック関数を実行するメカニズムを提供するAPIがlibeventであり、イベントドリブンなネットワークサービス内のイベントループを置き換えるもの、とのこと。
確かに、C++ってイベントみたいな機構って、コールバックしかないし、それらをモジュールで積極的に使うってシーンがないから、こういうライブラリはありがたいのかも。
詳しい説明は例のごとくWikipedia。

入手先は、公式ページのDownload–Stable releasesから。
2017/04/09時点で、2.1.8が安定版の最新です。
ソースからビルドするタイプです。
ビルドは簡単で、

  1. スタート > 全てのプログラム > Visual Studio 2015 > 開発者コマンド プロンプト for VS2015 を開く
  2. libeventの展開先フォルダに移動
  3. nmake -f Makefile.nmake を実行

以上です。

終了時に、

1
2
3
4
NMAKE : fatal error U1073: 'print-winsock-errors.obj' のビルド方法が指定されていません。
Stop.
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\nmake.exe"' : リターン コード '0x2'
Stop.

と表示されますが、

  • libevent.lib
  • libevent_core.lib
  • libevent_extras.lib

が、フォルダに出来上がっています。

Apache Thrift & Compiler

ソースとコンパイラーを入手します。
Downloadから、thrift-0.10.0.tar.gzThrift compiler for Windowsをダウンロードします。
2017/04/09時点で、0.10.0が最新です。
コンパイラーといっても、gRPC toolのように、定義ファイルからコードを生成するだけのものです。
ソースは、任意の場所に展開します。
展開先のlib\cppthrift.slnがあるので、Visual Studioで起動します。

libthrift

libthriftのプロパティを開きます。
C/C++ -> 全般を開き、追加のインクルード ディレクトリに下記を追加します。

1
2
3
{boost_install_dir}\boost_X_YZ_0
{boost_install_dir}\boost_X_YZ_0\boost
{openssl_install_dir}\include

ライブラリアン -> 全般を開き、追加のライブラリ ディレクトリに下記を追加します。>を開き、追加のライブラリ ディレクトリに下記を追加します。

1
{openss_install_dir}\lib

最後に、libthrift\concurrency\BoostThreadFactory.cppをプロジェクトから除外します。

libthriftnb

libthriftのプロパティを開きます。
C/C++ -> 全般を開き、追加のインクルード ディレクトリに下記を追加します。

1
2
3
4
{boost_install_dir}
{libevent_install_dir}
{libevent_install_dir}\include
{libevent_install_dir}\WIN32-Code\nmake

以上の設定を終えたら、ビルドが可能になります。
ソリューションでDebug/Release/Debug-mt/Release-mt、ターゲットプラットフォーム(x64/Win32)を選択してビルドします。

  • libthriftnb.lib
  • libthrift.lib

が生成されているはずです。

Conclusion

今回は導入のみ。
次回は、C#-C++で通信してみます。