読者です 読者をやめる 読者になる 読者になる

とりあえず prim_inet:async_accept を使った echo

MPM 方式に切り替えますが、とりあえず prim_inet:async_accept を使ってみたかったので。
prim_inet:async_accept とかドキュメントがなさ過ぎる物を使いまくっています:-P

コードは動きますが、結構適当です。無保証で:-P

tcp_server.erl

-module(tcp_server).

-behaviour(gen_server).

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

-export([echo/1]).

-record(state, {sock, ref, callback}).

echo(Socket) ->
  inet:setopts(Socket, [{active, once}]),
  receive
    {tcp, Socket, Packet} ->
      error_logger:info_msg("echo:tcp ~p\n", [Packet]),
      gen_tcp:send(Socket, Packet),
      echo(Socket);
    {tcp_closed, Socket} ->
      error_logger:info_msg("echo:tcp_closed"),
      gen_tcp:close(Socket);
    {tcp_error, Socket, Reason} ->
      error_logger:info_msg("echo:tcp_error ~p~n", [Reason]),
      gen_tcp:close(Socket)
  end.

start_link(IPAddress, Port, Callback) ->
  gen_server:start_link(?MODULE, {IPAddress, Port, Callback}, []).

init({IPAddress, Port, Callback}) ->
  process_flag(trap_exit, true),
  Opts = [binary, {packet, 2}, {reuseaddr, true}, {active, once}],
  case gen_tcp:listen(Port, Opts) of
    {ok, LSock} ->
      {ok, {LIPAddress, LPort}} = inet:sockname(LSock),
      error_logger:info_msg("started TCP listener on ~s:~p~n",
                            [inet_parse:ntoa(LIPAddress), LPort]),
      {ok, Ref} = prim_inet:async_accept(LSock, -1),
      {ok, #state{sock=LSock, ref=Ref, callback=Callback}};
    {error, Reason} ->
      error_logger:error_msg(
        "failed to start TCP listener on ~s:~p - ~p~n",
        [inet_parse:ntoa(IPAddress), Port, Reason]),
            {stop, {cannot_listen, IPAddress, Port, Reason}}
  end.

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

handle_cast(_Msg, State) ->
  {noreply, State}.

handle_info({inet_async, LSock, Ref, {ok, Sock}},
            State=#state{sock=LSock, ref=Ref, callback={M,F,A}}) ->
  {ok, Mod} = inet_db:lookup_socket(LSock),
  inet_db:register_socket(Sock, Mod),

  {ok, {Address, Port}} = inet:sockname(LSock),
  {ok, {PeerAddress, PeerPort}} = inet:peername(Sock),
  error_logger:info_msg("accepted TCP connection on ~s:~p from ~s:~p~n",
                        [inet_parse:ntoa(Address), Port,
                         inet_parse:ntoa(PeerAddress), PeerPort]),

  apply(M, F, A ++ [Sock]),

  case prim_inet:async_accept(LSock, -1) of
    {ok, NewRef} ->
      {noreply, State#state{ref=NewRef}};
    Error ->
      {stop, {cannot_accept, Error}, none}
  end;

handle_info({inet_async, LSock, Ref, {error, closed}},
            State=#state{sock=LSock, ref=Ref}) ->
  {stop, normal, State};

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

terminate(_Reason, State) ->
  gen_tcp:close(State#state.sock),
  ok.

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

追記

supervisor に置いた。

tcp_server_sup.erl

-module(tcp_server_sup).

-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

start_link() ->
  IPAddress = "127.0.0.1",
  Port = 5000,
  Callback = {tcp_server, echo, []}, 
  supervisor:start_link(?MODULE, {IPAddress, Port, Callback}).

init({IPAddress, Port, Callback}) ->
  {ok, {{one_for_all, 10, 10},
      [{tcp_listener, {tcp_server, start_link, [IPAddress, Port, Callback]},
          permanent, brutal_kill, worker, [tcp_listener]}]}}.