Hatena::Grouperlang

lnzntの Erlang 日記 このページをアンテナに追加 RSSフィード

2011年08月14日

いろはメモ - Erlang 編 - 「に」 : TCP による通信

12:56 | いろはメモ - Erlang 編 - 「に」 : TCP による通信 - lnzntの Erlang 日記 を含むブックマーク はてなブックマーク - いろはメモ - Erlang 編 - 「に」 : TCP による通信 - lnzntの Erlang 日記 いろはメモ - Erlang 編 - 「に」 : TCP による通信 - lnzntの Erlang 日記 のブックマークコメント

いろはメモ - 初心者の書いたメモです。間違いは随時直していきます。

----

TCP サーバ

シンプルすぎる TCP サーバの例。ポート 50001 を listen する。

-module(tcpserver).
-compile(export_all).

start() ->                %% データの型に list を指定、パケット化はしない
    {ok, Listen} = gen_tcp:listen(50001, [list,{packet,raw}]),
    {ok, Socket} = gen_tcp:accept(Listen),

    %% ソケットのデータはコネクションを accept したプロセス(=制御プロセス)に届く
    receive
        {tcp, Socket, Bin} ->      %% データ受信のパターン
            io:format("received : ~p~n", [Bin]);
        {tcp_closed, Socket} ->    %% コネクションクローズのパターン
            io:format("socket closed: [~w]~n", [Socket])
    end,

    gen_tcp:send(Socket, "bye\n"),

    gen_tcp:shutdown(Socket, read_write),
    gen_tcp:close(Socket),

    gen_tcp:shutdown(Listen, read_write),
    gen_tcp:close(Listen).

erl で実行してみる。

1> c(tcpserver).
{ok,tcpserver}
2> tcpserver:start().
                       %% ここでメッセージ待ちになる

telnet で "hello" を送信してみる。

$ telnet localhost 50001
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello                       # "hello" を入力して Enter
bye                         # サーバからの応答
Connection closed by foreign host.

erl 側。

   :
2> tcpserver:start().
received : "hello\r\n"     %% telnet から届いたメッセージ
ok
TCP クライアント

今度はクライアント

-module(tcpclient).
-compile(export_all).

start() ->
    {ok, Socket} = gen_tcp:connect("localhost", 50001, [list,{packet,raw}]),
    gen_tcp:send(Socket, "hello"),

    %% ソケットのデータはコネクションを connect したプロセス(=制御プロセス)に届く
    receive
        {tcp, Socket, Bin} ->    %% データ受信のパターン
                io:format("received : ~p~n", [Bin])
    end,

    gen_tcp:shutdown(Socket, read_write),
    gen_tcp:close(Socket).

さっきのサーバをもう一度起動*1

2> tcpserver:start().
                   %% ...待機状態になる。

別の erl を起動してクライアントを実行。

$ erl
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4  (abort with ^G)
1> c(tcpclient).
{ok,tcpclient}
2> tcpclient:start().
received : "bye\n"      %% サーバからの応答
ok

サーバの方にも、クライアントからのメッセージが届いている。

11> tcpserver:start().
received : "hello"
ok

gen_tcp:listen や gen_tcp:connect のオプションには以下のようなものが設定できる。

  • データの型。上の例では list を指定している。他の型は binary (他にもある?)
  • パケット化。上の例ではパケット化してない。{packet, 4} など指定できる
  • ソケットオプション。{reuseaddr, true} など指定できる
  • ソケットの制御モード。{active, true}、{active, false}, {active,once} のいずれか

ソケットの制御モード。デフォルトは {active, true} (らしい)。

{active, true}
アクティブ受信(ノンブロッキングモード)。サーバはブロックすることなくクライアントの要求を受けつける。(限界を超えるとマズい)
{active, false}
パッシブ受信(ブロッキングモード)。サーバが recv を呼び出すまでクライアントはブロックされる。(いくらかのバッファリングはある)
{active, once}
ハイブリッド手法(限定ブロッキング)。1つのメッセージだけアクティブになる。次のメッセージを受信できるようにするには明示的に inet:setopts を呼び出す必要がある。

---

参考リンク

Erlangリファレンスマニュアル

----

参考書籍

プログラミングErlang

プログラミングErlang

オーム社のページ(サンプルソースダウンロードなど) : Ohmsha | 商品一覧

*1:EADDRINUSE のエラーが出る場合は少し待ってから起動