Message Queue の実装でもよく使われているErlang。メッセージの送受信と、外部プログラムのインターフェイスを一緒にテストしてみました。
環境: Erlang (BEAM) emulator version 5.9.2 / Mac 10.10.5
参考:
http://erlang.org/doc/tutorial/c_port.html
http://d.hatena.ne.jp/m-hiyama/20070704/1183510151
http://d.hatena.ne.jp/m-hiyama/20070710/1184035643
receiver.erl
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 |
-module(receiver). -compile(export_all). start(ExtPrg) -> spawn(?MODULE, init, [ExtPrg]). init(ExtPrg) -> register(rcv, self()), process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, [{packet, 2}]), receiver_main(Port). receiver_main(Port) -> receive stop -> io:fwrite("~w:bye bye.\n", [self()]), exit(ok); {msg, Pid, Message} -> Port ! {self(), {command, Message}}, receive {Port, {data, Data}} -> Pid ! {msg, self(), Data} end; Other -> io:fwrite("Oops! ~w is ignored.\n", [Other]) end, receiver_main(Port). |
sender.erl
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 |
-module(sender). -compile(export_all). -define(REMOTE_NODE, 'node2@(hostname)'). start() -> spawn(?MODULE, sender_main, []). sender_main() -> io:fwrite("Sending message \"Hello\" to {rcv, ~w}.\n", [?REMOTE_NODE]), case net_adm:ping(?REMOTE_NODE) of pong -> io:fwrite("Remote node is up, "); bang -> io:fwrite("Remote node is not up.\n"), exit(ng) end, {rcv, ?REMOTE_NODE} ! {msg, self(), "Hello"}, io:fwrite("sent.\n"), io:fwrite("Waiting for a message ..."), receive {msg, Pid, Message} -> io:fwrite("Receive! From: ~w Msg: ~s\n", [Pid, Message]); _Any -> io:fwrite(" received! bye bye.\n"), exit(ok) end. |
(hostname)は環境に合わせる
receiverから実行するCプログラム
port.c
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 |
#include <stdio.h> #include <string.h> typedef unsigned char byte; int main() { int fn, arg, res = 1; byte buf[100]; while (1) { memset(buf, 0, sizeof(buf)); int sz = read_cmd(buf); if(sz<= 0) { break; } fprintf(stderr, "size : %d\r\n", sz); for(int i = 0 ; i < 10 ; i++){ fprintf(stderr, "%d ", buf[i]); } fprintf(stderr, "\r\n"); for(int j = 0 ; j < sz ; j++){ buf[j] = buf[j] + 1; } write_cmd(buf, sz); } } |
comm.c
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 |
typedef unsigned char byte; int read_cmd(byte *buf) { int len; if(read_exact(buf, 2) != 2) return(-1); len = (buf[0] << 8) | buf[1]; return read_exact(buf, len); } int write_cmd(byte *buf, int len) { byte li; li = (len >> 8) & 0xff; write_exact(&li, 1); li = len & 0xff; write_exact(&li, 1); return write_exact(buf, len); } int read_exact(byte *buf, int len) { int i, got=0; do { if((i = read(0, buf+got, len-got)) <= 0) return(i); got += i; } while (got<len); return(len); } int write_exact(byte *buf, int len) { int i, wrote = 0; do { if((i = write(1, buf+wrote, len-wrote)) <= 0) return (i); wrote += i; } while (wrote<len); return (len); } |
コンパイル
gcc -o extprg port.c comm.c -ansi
実行
受信側
erl -sname node2
Eshell> c(receiver).
{ok,receiver}
Eshell> receiver:start(“./extprg”).
<0.44.0>
%受信待ち->送信されると以下表示
size : 5
72 101 108 108 111 0 0 0 0 0
送信側
erl -sname node1
Eshell> c(sender).
{ok,sender}
Eshell> sender:start().
Sending message “Hello” to {rcv, ‘node2@(hostname)’}.
<0.44.0>
Remote node is up, sent.
Waiting for a message …Receive! From: <6939.44.0> Msg: Ifmmp
Cプログラムではデータをインクリメントして値を返すので、HelloがIfmmpとなります。
受信側で登録したrcvが送信側のプログラムで参照されているところがポイントだと思います。テストはここではローカルだけですが、Mac-Ubuntu間も確認済み。(cookieの設定の仕方とかも、上記サイトにあります。)
この共有されたrcvについて考えてみると、Erlangの分散処理の性質を感られる気がします。Erlangはとても興味があり、
http://decode.red/net/archives/140
http://bitlife.me/archives/231
上の記事につづいて、取り上げてみました。