erlang otp实现tcp_server—echo服务器

首先使用rebar构建名为tcp的工程


./rebar creat-app appid=tcp_server_app_app

目录如下:


├── 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

tcp_server_app_app.erl文件如下:


-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.

tcp_server_sup.erl文件如下:


-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}}.

tcp_server_handler.erl如下


-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}.

tcp_client.erl是客户端文件


-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).

测试过程

erlang otp实现tcp_server—echo服务器_第1张图片
屏幕快照 2015-04-22 下午8.55.48.png

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

你可能感兴趣的:(erlang otp实现tcp_server—echo服务器)