<div dir="ltr">Hi everyone,<div><br></div><div style>I'm trying to what is the best strategy for tcp socket acceptors. Just after brief research I found 2 approaches:</div><div style><br></div><div style>1. Pool of acceptors that can work in parallel. According to documentation ListenSocket can be used not only in the process where this socket was created.</div>

<div style><br></div><div style>Example code (from <a href="http://learnyousomeerlang.com/buckets-of-sockets">http://learnyousomeerlang.com/buckets-of-sockets</a>):<br></div><div style><br></div>%%% The supervisor in charge of all the socket acceptors.<br>

-module(sockserv_sup).<br>-behaviour(supervisor).<br><br>init([]) -><br>    .....<br><div>    {ok, ListenSocket} = gen_tcp:listen(Port, [{active,once}, {packet,line}]),<br>    spawn_link(fun empty_listeners/0),<br>    {ok, {{simple_one_for_one, 60, 3600},<br>

         [{socket,<br>          {sockserv_serv, start_link, [ListenSocket]}, % pass the socket!<br>          temporary, 1000, worker, [sockserv_serv]}<br>         ]}}.<br><br></div><div>start_socket() -> supervisor:start_child(?MODULE, []).<br>

<br></div><div>empty_listeners() -> [start_socket() || _ <- lists:seq(1,20)], ok.<br><br>%%%%%%%%%%%%%%%%%%<br>%% socket acceptor<br><pre>-module(sockserv_serv).<br>-behaviour(gen_server).</pre></div><div>init(Socket) -><br>

    ....</div><div>    gen_server:cast(self(), accept),<br>    {ok, #state{socket=Socket}}.<br><br>handle_call(_E, _From, State) ->  {noreply, State}.<br><br>handle_cast(accept, S = #state{socket=ListenSocket}) -><br>

    {ok, AcceptSocket} = gen_tcp:accept(ListenSocket),<br>    sockserv_sup:start_socket(), % a new acceptor is born, praise the lord<br>    {noreply, S#state{socket=AcceptSocket, next=name}};</div><div><font size="1">---------------------------------</font></div>

<div><font size="1"><br></font></div><div style>As I examined code of mochiweb (<a href="https://github.com/mochi/mochiweb/blob/master/src/mochiweb_socket_server.erl">https://github.com/mochi/mochiweb/blob/master/src/mochiweb_socket_server.erl</a>, <a href="https://github.com/mochi/mochiweb/blob/master/src/mochiweb_acceptor.erl">https://github.com/mochi/mochiweb/blob/master/src/mochiweb_acceptor.erl</a>) and ranch (<a href="https://github.com/extend/ranch">https://github.com/extend/ranch</a>) they use the same approach.</div>

<div><font size="1"><br></font></div><div style>2. Using undocumented function prim_inet:async_accept</div><div style><br></div><div style>Example code (from <a href="http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles">http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles</a>)</div>

<div style><br></div><div><pre style="margin:auto;border:1px solid rgb(134,4,4);padding:3px;background-color:rgb(255,255,221);color:rgb(0,0,0);font-size:1em;word-wrap:break-word;line-height:16.700000762939453px">init([Port, Module]) ->
    process_flag(trap_exit, true),
    Opts = [binary, {packet, 2}, {reuseaddr, true},
            {keepalive, true}, {backlog, 30}, {active, false}],
    case gen_tcp:listen(Port, Opts) of
    {ok, Listen_socket} ->
        %%Create first accepting process
        {ok, Ref} = prim_inet:async_accept(Listen_socket, -1),
        {ok, #state{listener = Listen_socket,
                    acceptor = Ref,
                    module   = Module}};
    {error, Reason} ->
        {stop, Reason}
    end.</pre></div><div><font size="1"><br></font></div><div><pre style="margin:auto;border:1px solid rgb(134,4,4);padding:3px;background-color:rgb(255,255,221);color:rgb(0,0,0);font-size:1em;word-wrap:break-word;line-height:16.700000762939453px">

handle_info({inet_async, ListSock, Ref, {ok, CliSocket}},
            #state{listener=ListSock, acceptor=Ref, module=Module} = State) ->
    try
        case set_sockopt(ListSock, CliSocket) of
        ok              -> ok;
        {error, Reason} -> exit({set_sockopt, Reason})
        end,

        %% New client connected - spawn a new process using the simple_one_for_one
        %% supervisor.
        {ok, Pid} = tcp_server_app:start_client(),
        gen_tcp:controlling_process(CliSocket, Pid),
        %% Instruct the new FSM that it owns the socket.
        Module:set_socket(Pid, CliSocket),

        %% Signal the network driver that we are ready to accept another connection
        case prim_inet:async_accept(ListSock, -1) of
        {ok,    NewRef} -> ok;
        {error, NewRef} -> exit({async_accept, inet:format_error(NewRef)})
        end,

        {noreply, State#state{acceptor=NewRef}}
    catch exit:Why ->
        error_logger:error_msg("Error in async accept: ~p.\n", [Why]),
        {stop, Why, State}
    end;</pre></div><div><font size="1"><br></font><div><div><div style>It is used by erlyvideo (<a href="https://github.com/erlyvideo/erlyvideo/blob/master/apps/erlyvideo/src/core/gen_listener.erl">https://github.com/erlyvideo/erlyvideo/blob/master/apps/erlyvideo/src/core/gen_listener.erl</a>).</div>

<div style><br></div><div style>Can you give some pros and cons of both approaches?</div><div style><br></div><div style>With best wishes,</div><div>Andrey Tereskin</div></div>
</div></div></div>