home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

Erlang/exercie/echo_server, client (v1)

Erlang/exercie/echo_server, client (v1)

Erlang / exercie / echo_server, client (v1)
id: 420 所有者: msakamoto-sf    作成日: 2009-08-07 13:17:09
カテゴリ: Erlang ネットワーク 

ErlangでTCP通信を処理する練習として、簡単な"echo"サーバ/クライアントを書いてみた。

echo_server.erl

-module(echo_server).
-export([start/1, stop/1]).

start(Port) when is_integer(Port) ->
    spawn(fun() -> init(Port) end).

stop(Pid) -> Pid ! {self(), stop}.

init(Port) ->
    {ok, ListenSocket} = gen_tcp:listen(Port, 
        [binary, {active, true}, {reuseaddr, true}, {packet, line}]),
    Pid = spawn(fun() -> accept_spawn(ListenSocket) end),
    io:format("new accept_spawn : ~p~n", [Pid]),
    loop(ListenSocket).

loop(ListenSocket) ->
    receive
        {_From, stop} ->
            gen_tcp:close(ListenSocket),
            io:format("closed: ~p~n", [ListenSocket]);
        {_From, Other} ->
            io:format("unknown message: ~p~n", [Other]),
            loop(ListenSocket)
        end.

accept_spawn(ListenSocket) ->
    case gen_tcp:accept(ListenSocket) of
        {ok, Socket} ->
            {ok, {Host, Port}} = inet:peername(Socket),
            io:format("~p:connected from ~p, port ~p~n", [self(), Host, Port]),
            Pid = spawn(fun() -> accept_spawn(ListenSocket) end),
            io:format("new accept_spawn : ~p~n", [Pid]),
            echo_proc(Socket);
        {error, Why} ->
            io:format("accept() error: ~p~n", [Why])
    end.

echo_proc(Socket) ->
    receive
        {tcp, Socket, Bin} ->
            io:format("~p:read and echo [~s]~n", [self(), Bin]),
            gen_tcp:send(Socket, Bin),
            echo_proc(Socket);
        {tcp_closed, Socket} ->
            io:format("~p:closed from client.~n", [self()])
    after 4000 ->
        io:format("~p:read timeout~n", [self()]),
        gen_tcp:close(Socket)
    end.

echo_client.erl

-module(echo_client).
-export([connect/2]).

connect(Host, Port) when is_list(Host), is_integer(Port) ->
    {ok, Socket} = gen_tcp:connect(Host, Port, [binary, {active, false}]),
    io:format("connected: ~p~n", [Socket]),
    io:format("input 'quit' for disconnection.~n", []),
    loop(Socket).

loop(Socket) ->
    case io:get_line("echo> ") of
        eof -> disconnect(Socket);
        Str when Str =:= "quit\n" -> disconnect(Socket);
        Str -> 
            case gen_tcp:send(Socket, Str) of
                ok -> pass;
                {error, Why} ->
                    io:format("send() error: ~p~n", [Why]),
                    disconnect(Socket)
            end,
            case gen_tcp:recv(Socket, 0) of
                {ok, Reply} ->
                    io:format("Reply = ~s", [binary_to_list(Reply)]),
                    loop(Socket);
                {error, Why2} ->
                    io:format("recv() error: ~p~n", [Why2]),
                    disconnect(Socket)
            end
    end.

disconnect(Socket) ->
    io:format("disconnect from ~p~n", [Socket]),
    gen_tcp:close(Socket).

動かし方

サーバーを動かしてみる

DOS> erl
Eshell V5.6.3  (abort with ^G)
1> Pid = echo_server:start(1234).
<0.31.0>
2> new accept_spawn : <0.33.0>

ここで適当にtelnetクライアントを立ち上げ、localhostのポート1234宛に接続し、"foo\n", "bar\n"と続けて送信し、クライアント側から切断してみる。その時のサーバ側の出力は次のようになる。

2> <0.33.0>:connected from {127,0,0,1}, port 4084
2> new accept_spawn : <0.34.0>
2> <0.33.0>:read and echo [foo
]
2> <0.33.0>:read and echo [bar
]
2> <0.33.0>:closed from client.

今回作ってみたecho_serverは、4000msの受信タイムアウトを設定している。実際にtelnetで接続し、数バイト送信後に4秒以上放置してみる。

2> <0.34.0>:connected from {127,0,0,1}, port 4102
2> new accept_spawn : <0.35.0>
2> <0.34.0>:read and echo [foo
]
2> <0.34.0>:read timeout

echo_serverを停止する。

2> echo_server:stop(Pid).
{<0.29.0>,stop}
accept() error: closed
closed: #Port<0.97>

最後にaccept()エラーが出ているのは、gen_tcp:accept()で待っているプロセスがあるため。
エラーを出さずに終わらせたいが、上手な方法が分からない・・・。

クライアント側を動かしてみる

別の端末なりプロンプトでerlを立ち上げ、echo_client:connect/2を実行する。

DOS> erl
Eshell V5.6.3  (abort with ^G)
1> echo_client:connect("localhost", 1234).
connected: #Port<0.205>
input 'quit' for disconnection.
echo> foo
Reply = foo
echo> bar
Reply = bar
echo> quit
disconnect from #Port<0.205>
ok

タイムアウトを簡単に実装できて便利なErlang



プレーンテキスト形式でダウンロード
表示中のバージョン : 1
現在のバージョン : 2
更新者: msakamoto-sf
更新日: 2009-08-07 14:34:39
md5:4726144628fcdf60519aeeeb5890b5a3
sha1:c390733bbcb55e71513e8a45a72778d4dba04aed
コメント
コメントを投稿するにはログインして下さい。