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