[erlang-questions] tcp connections dropped in gen_server

Ladislav Lenart <>
Mon Sep 5 20:51:57 CEST 2011


Hello.

I think the problem of missing log messages (and hanging connection
processes) might be in the fact that you don't handle messages of
the form {tcp_error, Socket, Reason} in your loop/1 function.

Also, if I understand the code correctly, the newly created connection
processes (acceptors) are not supervised. To prevent future problems
with this I strongly recommend you to modify your code slightly as
suggested in the book "Erlang and OTP in action".

NOTE: I haven't even attempted to compile the following code (taken
from the book and adapted to your use case).

Modified process structure:
     simple_one_for_one - one for each ListenSocket
         loop - one for each existing TCP connection on the ListenSocket
         acceptor - one on the ListenSocket


%%%%%%%%%%%%%%%%%%%%%%
%%% TCP supervisor %%%
%%%%%%%%%%%%%%%%%%%%%%
-module(tcp_sup).

-behaviour(supervisor).

-export([start_link/1, start_child/1]).
-export([init/1]).

start_link({port, Port}) ->
     {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {reuseaddr, true}, {active, true}]),
     start_link({listen_socket, ListenSocket});
start_link({listen_socket, ListenSocket}) ->
     supervisor:start_link(?MODULE, [ListenSocket]).

start_child(SupPid) ->
     supervisor:start_child(SupPid, []).

init([ListenSocket]) ->
     Server = {tcp_srv, {tcp_srv, start_link, [self(), ListenSocket]},
               temporary, brutal_kill, worker, [tcp_srv]},
     RestartStrategy = {simple_one_for_one, 0, 1},    % <-- tune for production demands
     {ok, {RestartStrategy, [Server]}}.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% TCP server (aceptor + loop for one TCP connection %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(tcp_srv).

-export([start_link/2]).
-export([acceptor/2]).

start_link(SupPid, ListenSocket) ->
     proc_lib:spawn_link(?MODULE, acceptor, [SupPid, ListenSocket]).

acceptor(SupPid, ListenSocket) ->
     {ok, Socket} = gen_tcp:accept(ListenSocket),
     tcp_sup:start_child(SupPid, ListenSocket),    % <-- Instruct the tcp_sup SupPid to start new acceptor process.
     error_logger:info_msg("New connection from ~p~n", [Socket]),
     inet:setopts(Socket, [binary, {nodelay, true}, {active, true}]),
     loop(Socket).

loop(Socket) ->
     %% As before.


You should also consider to introduce a flow control to limit
unbounded memory usage under heavy load using {active, false}
for ListenSocket and {active, once} for Socket:
1. [in tcp_sup:start_link/1] {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {reuseaddr, true}, {active, false}]),
2. [in tcp_srv:acceptor/2] inet:setopts(Socket, [binary, {nodelay, true}, {active, once}]),
3. [modify tcp_srv:loop/1]
    loop(Socket) ->
        receive
            {tcp, Socket, Data} ->
                inet:setopts(Socket, [{active, once}]),    % <-- added line
                error_logger:info_msg("Messaged received from ~p: ~p~n", [Socket, Data]),
                comm_lib:handle_message(Socket, Data),
                loop(Socket);
            {tcp_closed, Socket} ->
                error_logger:info_msg("Device at ~p disconnected~n", [Socket]);
            _Any ->
                %% skip this
                loop(Socket)
        end.


HTH,

Ladislav Lenart


On 5.9.2011 18:59, Reynaldo Baquerizo wrote:
> I have a running application that consist in a supervisor and two
> generic servers, one of them wraps around odbc and the other handles
> tcp connections, a fragment of the relevant code is:
>
>
> init([]) ->
>      process_flag(trap_exit, true),
>      {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {packet, 0},
> 								     {reuseaddr, true},
> 								     {active, true}]),
>      proc_lib:spawn_link(?MODULE, acceptor, [ListenSocket])
>
> acceptor(ListenSocket) ->
>      {ok, Socket} = gen_tcp:accept(ListenSocket),
>      error_logger:info_msg("New connection from ~p~n", [Socket]),
>      _Pid = proc_lib:spawn(?MODULE, acceptor, [ListenSocket]),
>      inet:setopts(Socket, [binary, {nodelay, true}, {active, true}]),
>      loop(Socket).
>
> loop(Socket) ->
>      receive
> 	{tcp, Socket, Data} ->
> 	error_logger:info_msg("Messaged received from ~p: ~p~n", [Socket, Data]),
> 	    comm_lib:handle_message(Socket, Data),
> 	    loop(Socket);
> 	{tcp_closed, Socket} ->
> 	error_logger:info_msg("Device at ~p disconnected~n", [Socket]);
> 	_Any ->
> 	%% skip this
> 	    loop(Socket)
>      end.
>
> So, I basically start a new  unlinked process for every new tcp
> connection. It works just fine for a couple hours but  then every tcp
> connection is dropped gradually with message "Device at ~p
> disconnected". The client will try to reconnect if connection is
> closed. The tcp connection should only terminate if remote end closes
> it or spawned proccess in the server crashes.
>
> After all connections were dropped, I can see with inet:i() that there
> are established connections but no logging!
>
> Can anyone give some insight or point to the right direction to debug this?
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions
>
>





More information about the erlang-questions mailing list