美文网首页
erlang otp实现tcp_server—echo服务器

erlang otp实现tcp_server—echo服务器

作者: randyjia | 来源:发表于2015-04-22 20:46 被阅读594次

    首先使用rebar构建名为tcp的工程
    <pre>
    ./rebar creat-app appid=tcp_server_app_app
    </pre>

    目录如下:
    <pre>
    ├── ebin
    │ ├── tcp_client.beam
    │ ├── tcp_server_app.app
    │ ├── tcp_server_app_app.beam
    │ ├── tcp_server_app_sup.beam
    │ ├── tcp_server_handler.beam
    │ └── tcp_server_sup.beam
    ├── rebar
    ├── src
    │ ├── client
    │ ├── tcp_server_app.app.src
    │ ├── tcp_server_app_app.erl
    │ ├── tcp_server_app_sup.erl
    │ ├── tcp_server_handler.erl
    │ └── tcp_server_sup.erl
    └── tcp_server_app.iml
    </pre>

    tcp_server_app_app.erl文件如下:
    <pre>
    -module(tcp_server_app_app).

    -behaviour(application).

    -export([start/2, stop/1]).
    -define(DEF_PORT, 30000).

    start(_StartType, _StartArgs) ->
    Opts = [binary, {packet, 2}, {reuseaddr, true},
    {keepalive, true}, {backlog, 30}, {active, false}],
    ListenPort = ?DEF_PORT,
    {ok, LSock} = gen_tcp:listen(ListenPort, Opts),
    case tcp_server_sup:start_link(LSock) of
    {ok, Pid} ->
    tcp_server_sup:start_child(),
    {ok, Pid};
    Other ->
    {error, Other}
    end.

    stop(_State) ->
    ok.
    </pre>

    tcp_server_sup.erl文件如下:
    <pre>
    -module(tcp_server_sup).
    -author("mohe").

    -behaviour(supervisor).

    %% API
    -export([start_link/1, start_child/0]).

    %% Supervisor callbacks
    -export([init/1]).

    -define(SERVER, ?MODULE).

    start_link(LSock) ->
    supervisor:start_link({local, ?SERVER}, ?MODULE, [LSock]).

    start_child() ->
    supervisor:start_child(?SERVER, []).

    init([LSock]) ->
    io:format("tcp_server_sup init ~n"),
    Server = {tcp_server_handler, {tcp_server_handler, start_link, [LSock]},
    temporary, brutal_kill, worker, [tcp_server_handler]},
    Children = [Server],
    RestartStrategy = {simple_one_for_one, 0, 1},
    {ok, {RestartStrategy, Children}}.

    </pre>

    tcp_server_handler.erl如下
    <pre>
    -module(tcp_server_handler).
    -author("mohe").

    -behaviour(gen_server).

    %% API
    -export([start_link/1]).

    %% gen_server callbacks
    -export([init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3]).

    -define(SERVER, ?MODULE).

    -record(state, {lsock, socket, addr}).

    start_link(LSock) ->
    io:format("tcp_server_handler start_link ~n"),
    gen_server:start_link(?MODULE, [LSock], []).

    init([Socket]) ->
    io:format("tcp_server_handler init ~n"),
    inet:setopts(Socket, [{active, once}, {packet, 2}, binary]),
    {ok, #state{lsock = Socket}, 0}.

    handle_call(Msg, _From, State) ->
    {reply, {ok, Msg}, State}.

    handle_cast(stop, State) ->
    {stop, normal, State}.

    handle_info({tcp, Socket, Data}, State) ->
    inet:setopts(Socket, [{active, once}]),
    io:format("receive message from client ~p ~n", [Data]),
    ok = gen_tcp:send(Socket, <<"Echo back : ", Data/binary>>),
    {noreply, State};

    handle_info({tcp_closed, _}, #state{addr = Addr} = StateData) ->
    io:format("~p Client ~p disconnected ~n", [self(), Addr]),
    {stop, normal, StateData};

    handle_info(timeout, #state{lsock = LSock} = State) ->
    {ok, ClientSocket} = gen_tcp:accept(LSock), %阻塞在此
    {ok, {IP, _Port}} = inet:peername(ClientSocket),
    tcp_server_sup:start_child(), %再次启动一个tcp_server_handler
    {noreply, State#state{socket = ClientSocket, addr = IP}};

    handle_info(_Info, StateData) ->
    {noreply, StateData}.

    terminate(_Reason, #state{socket = Socket}) ->
    (catch gen_tcp:close(Socket)),
    ok.

    code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

    </pre>

    tcp_client.erl是客户端文件
    <pre>
    -module(tcp_client).
    -author("mohe").

    -behaviour(gen_server).

    -export([start/0, send/1, stop/0]).

    -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

    -record(state, {socket}).

    start() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

    init([]) ->
    {ok, Socket} = gen_tcp:connect("127.0.0.1", 30000, [binary, {packet, 2}, {active, true}, {reuseaddr, true}]),
    {ok, #state{socket = Socket}}.

    handle_call(_Msg, _From, State) ->
    {noreply, ok, State}.

    handle_cast(stop, State) ->
    {stop, normal, State};

    handle_cast({send, Msg}, State) ->

    state{socket = Socket} = State,

    gen_tcp:send(Socket, Msg),
    {noreply, State}.

    handle_info({tcp, _, Bin}, State) ->
    io:format("receive data from server ~p ~n", [Bin]),
    {noreply, State};

    handle_info({tcp_closed, _S}, State) ->
    {noreply, State};

    handle_info(timeout, State) ->
    {ok, Socket} = gen_tcp:connect("127.0.0.1", 30000, [{active, true}]),
    {noreply, State#state{socket = Socket}};

    handle_info(Info, State) ->
    io:format("pn", [Info]),
    {noreply, State}.

    terminate(_R, #state{socket = Socket}) ->
    io:format("terminate~n"),
    (catch gen_tcp:close(Socket)),
    ok.

    code_change(_O, S, _E) -> {ok, S}.

    send(Msg) when is_list(Msg) orelse is_binary(Msg) ->
    gen_server:cast(?MODULE, {send, Msg}).

    stop() ->
    gen_server:cast(?MODULE, stop).
    </pre>

    测试过程

    屏幕快照 2015-04-22 下午8.55.48.png

    整个代码在https://gitcafe.com/jwjgauss/tcp_server_echo

    相关文章

      网友评论

          本文标题:erlang otp实现tcp_server—echo服务器

          本文链接:https://www.haomeiwen.com/subject/vegufttx.html