colin
colin

Reputation: 41

erlang socket (using ranch) close in a short time?

i use ranch to listen socket, but in a short time about five seconds, ranch closed the socket, and my setting of socket is above, so what' wrong?

    {ok, _} = ranch:start_listener(server,200, ranch_tcp, [{port, 5555},{active, once},    {max_connections, 1024}], server_protocol, []), %% start the listener

the protocol file is below, the ranch listen to accept a socket, and the reverse the receive data, but what's wrong is that, when send data back to the client, after about five seconds, the client receive the message says that the socket is closed by server, i don't know if is ranch's default settings cause this?

-module(reverse_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
%% API.
-export([start_link/4]).
%% gen_server.
-export([init/1]).
-export([init/4]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-define(TIMEOUT, 5000).
-record(state, {socket, transport}).

%% API.
start_link(Ref, Socket, Transport, Opts) ->
proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
%% gen_server.
%% This function is never called. We only define it so that
%% we can use the -behaviour(gen_server) attribute.

init([]) -> {ok, undefined}.

init(Ref, Socket, Transport, _Opts = []) ->
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}]),
gen_server:enter_loop(?MODULE, [],
    #state{socket=Socket, transport=Transport},
    ?TIMEOUT).

handle_info({tcp, Socket, Data}, State=#state{
    socket=Socket, transport=Transport}) ->
Transport:setopts(Socket, [{active, once}]),
Transport:send(Socket, reverse_binary(Data)),
{noreply, State, ?TIMEOUT};

handle_info({tcp_closed, _Socket}, State) ->
{stop, normal, State};

handle_info({tcp_error, _, Reason}, State) ->
{stop, Reason, State};

handle_info(timeout, State) ->
{stop, normal, State};

handle_info(_Info, State) ->
{stop, normal, State}.

handle_call(_Request, _From, State) ->
{reply, ok, State}.

handle_cast(_Msg, State) ->
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%% Internal.
reverse_binary(B) when is_binary(B) ->
[list_to_binary(lists:reverse(binary_to_list(
    binary:part(B, {0, byte_size(B)-2})
))), "\r\n"].

Upvotes: 2

Views: 1552

Answers (1)

Daniel
Daniel

Reputation: 491

So your problem is because your gen_server process is timing out and shutting down. The socket being closed is a side effect of this because ranch links the Socket to the spawned handler process.

Once the new process enters the gen_server loop with the call to gen_server:enter_loop, it has ?TIMEOUT milliseconds to receive a message before it is sent a timeout message.

-define(TIMEOUT, 5000).

init(Ref, Socket, Transport, _Opts = []) ->
  ok = proc_lib:init_ack({ok, self()}),
  ok = ranch:accept_ack(Ref),
  ok = Transport:setopts(Socket, [{active, once}]),
  gen_server:enter_loop(?MODULE, [],
    #state{socket=Socket, transport=Transport},
    ?TIMEOUT). %% timeout because of this!

handle_info({tcp, Socket, Data}, State=#state{
  socket=Socket, transport=Transport}) ->
  Transport:setopts(Socket, [{active, once}]),
  Transport:send(Socket, reverse_binary(Data)),
  {noreply, State, ?TIMEOUT}; %% timeout because of this!

So when those five second pass and the gen_server hasn't received any messages in that time it sends itself a timeout message, which is then handled by handle_info

handle_info(timeout, State) ->
  {stop, normal, State};

Your handle_info tells the gen_server to stop, which causes the Socket to close because the two are linked together.

You can either remove the timeouts completely, or just stop the timeout from causing the process to close.

Here is how I would change the handle_info timeout code:

handle_info(timeout, State) ->
  io:format("the socket is idle~n"),
  {noreply,State};

Upvotes: 2

Related Questions