[erlang-questions] tcp connections dropped in gen_server

Joseph Norton norton@REDACTED
Wed Sep 7 16:42:45 CEST 2011


Hi.  I can not help but offer a suggestion.  As one working example, the UBF framework contains a TCP/IP listener - called proc_socket_server.

https://github.com/norton/ubf/blob/master/src/proc_socket_server.erl

The structure is simple and effective and doesn't use supervisors or even gen_servers.  As previously suggested, supervisors adds unnecessary complexity (and possibly negative performance impact) than seems required for the task.

I suspect your worker process will be different than UBF's worker.  Nevertheless, the UBF worker's loop for reading and writing from the socket is simple and easy to follow.  The worker is called - contract_driver.

https://github.com/norton/ubf/blob/master/src/contract_driver.erl

regards,


Joseph Norton



On Sep 7, 2011, at 11:15 PM, Ladislav Lenart wrote:

> Hello.
> 
> 
> On 7.9.2011 12:55, Reynaldo Baquerizo wrote:
>>> [I hope it's ok that I reply to the list - someone else might find
>>> this information useful as well. I am mentioning this only because
>>> you keep replying to me privately.]
>> 
>> Ooops, apologies ... I certainly didn't mean to write to you alone.
> 
> No problem :-)
> 
> 
>>> It makes very little sense to restart these processes (to me), because
>>> the TCP connection will die as well. The external client can reconnect
>>> and start anew. Or am I missing something?
>> 
>> Indeed, the client will reconnect. But I think I found the problem. The
>> process that is listening for new connections in gen_tcp:accept/1 dies
>> at some point, all other processes with established connections are fine
>> but eventually crash (cause of bad input), no further reconnections
>> will be possible.
> 
> I see. Can you provide us with the exact cause of it (i.e. what
> gen_tcp:accept/1 returned)? It seems to me that under normal
> operation (if the accept was successful at least once before),
> there should be no problem of this kind...
> 
> 
>> How can I isolate the listening process? or reestructure to restart it
>> if it crashes?
> 
> You need to restructure the processes then:
> 
>    port (service) supervisor - one_to_one
>        acceptor - worker
>        session supervisor - simple_one_to_one
>            connection - worker
> 
> Notes:
> * The above is a complete hierarchy of one TCP service listening on
>   a given port.
> * There's exactly one acceptor under a port supervisor. The acceptor
>   creates a listening socket and calls gen_tcp:accept/1 on it. It's
>   terminated via brutal_kill (because of the blocking nature of the
>   gen_tcp:accept/1 call). If it crashes it will be restarted by the
>   port supervisor.
> * session_supervisor is basically a tcp_sup.
> * connection is essentially the loop(Socket) part of the previous
>   connection process. These don't have to be killed via brutal_kill
>   because they don't block.
> 
> All the "magic" happens in the acceptor process...
> 
> 
> %%%%%%%%%%%%%%%%%%%%
> %%% TCP acceptor %%%
> %%%%%%%%%%%%%%%%%%%%
> -module(tcp_acceptor).
> 
> start_link(SupPid, Port) ->
>    {ok, proc_lib:spawn_link(?MODULE, init, [SupPid, Port])}.
> 
> init(SupPid, Port) ->
>    %% IMPORTANT: active must be set to false.
>    {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {reuseaddr, true}, {active, false}]),
>    accept(SupPid, ListenSocket).
> 
> accept(SupPid, ListenSocket) ->
>    {ok, Socket} = gen_tcp:accept(ListenSocket),
>    start_connection(SupPid, Socket),
>    accept(SupPid, ListenSocket).
> 
> start_connection(SupPid, Socket) ->
>    %% tcp_session_sup is the id of the simple_one_to_one session supervisor
>    %% in the child specification of the port supervisor (SupPid here).
>    Kids = supervisor:which_children(SupPid),
>    {value, {tcp_session_sup, SessionSup, _, _}} = lists:keysearch(tcp_session_sup, 1, Kids),
>    {ok, Pid}} = supervisor:start_child(SessionSup, []),
>    %% Force Socket to send future messages to Pid and not to me.
>    ok = gen_tcp:controlling_process(Socket, Pid),
>    %% Inform Pid about Socket (let it initialize the TCP session).
>    %% As a bare minimum Pid should remember Socket in its internal state
>    %% and set active flag to one of {active, true} or {active, once}.
>    Pid ! {init_tcp_session, Socket}.
> 
> 
> I hope the rest of the picture is clear now. Note also that this can be
> turned into a generic TCP service / application fairly easily. You just
> need to parametrize the above with:
> * CallbackModule - name of the module that implements application specific
>   connection process on top of TCP. Session supervisor needs it to create
>   a desired child specification.
> * CallbackOptions - list of initial arguments passed to connection process
>   via message {init_tcp_session, Socket, CallbackOptions}.
> * PacketOptions - list of PacketOptions passed to tcp_acceptor.
> 
> 
> If you have further questions, please do not hesitate and ask! :-)
> 
> 
> Ladislav Lenart
> 
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions




More information about the erlang-questions mailing list