ErlangでUNIXドメインソケットのクライアント接続を行なう簡単な方法
Erlang(OTP-17.3)では標準でUNIXドメインソケットをサポートされておらず、ちゃんと使おうとすると外部ライブラリが必要だったり、自前でポートドライバを書く必要があったりして、結構面倒。
ただ、特に性能等を気にせず簡単に使いたいだけなら、ncコマンド(netcat)がクライアント機能を持っているので、単にそれをラップすれば良い。
ncコマンドでのUNIXドメインソケット使用方法
-U
オプションを付けることで、UNIXドメインソケットが扱えるようになる。
サーバ:
# ※ 一つ以上のクライアントは接続不可 $ nc -U -l /tmp/hoge.socket
クライアント:
$ nc -U /tmp/hoge.socket
サーバとクライアントの通信は標準入出力経由で行なう。
# サーバに"Hello World\n"とデータを送る $ echo "Hello World\n" | nc -U /tmp/hoge.socket
Erlangから使う方法
erlang:open_port/2
でncコマンドを呼び出すだけ。
-module(unix_socket). -export([connect/1, send/2, close/1]). %% @doc UNIXドメインソケット用のクライアント接続関数 -spec connect(string()) -> {ok, port()} | {error, Reason::term()}. connect(UnixDomainSocketPath) -> %% open_port/2を使って、ncコマンドを呼び出す Command = "/bin/nc", Port = erlang:open_port({spawn_executable, Command}, [{args, ["-U", UnixDomainSocketPath]}, stderr_to_stdout, binary, exit_status]), receive %% 50ms以内にコマンドが停止したら、connect失敗扱いにする {Port, {exit_status, Status}} -> receive {Port, {data, ExitReason}} -> {error, {abort, Command, [{status, Status}, {reason, ExitReason}]}} after 0 -> {error, {abort, Command, [{status, Status}]}} end after 50 -> %% 接続成功 {ok, Port} end. -spec send(port(), iodata()) -> ok | {error, Reason::term()}. send(Port, Data) -> try _ = erlang:port_command(Port, Data), ok catch error:Reason -> {error, Reason} end. -spec close(port()) -> ok. close(Port) -> erlang:port_close(Port).
使用例:
%% 事前に別のシェルで`nc -U -l /tmp/hoge.socket`が実行されているものとする %% 接続失敗: 存在しないパスを指定 > unix_socket:connect("/tmp/fuga.socket"). {error,{abort,"/bin/nc", [{status,1}, {reason,<<"nc: unix connect failed: No such file or directory\n">>}]}} %% 接続成功 > {ok, S} = unix_socket:connect("/tmp/hoge.socket"). {ok,#Port<0.23824>} %% データ送信 > unix_socket:send(S, <<"Hello\n">>). % サーバ側の端末に"Hello\n"と表示される ok %% データ受信: %% サーバ側の端末で"World\n"という文字列が入力されたものとする > flush(). Shell got {#Port<0.23824>,{data,<<"World\n">>}} ok %% 切断 > unix_socket:close(S). ok
デバッグ用途等であれば使えなくはない、程度のメモ書き。