[erlang-questions] tcp connections dropped in gen_server
Ladislav Lenart
lenartlad@REDACTED
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
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
>
>
More information about the erlang-questions
mailing list