erlang otp gen_server разрывает соединение, когда handle_call возвращает noreply

Запустите сервер, используя:

erlc server.erl ; erl -eval 'server:start()'

В другом терминале:

telnet localhost 3547

Что могло бы успешно установить соединение, но через несколько секунд соединение было закрыто сервером по независящим от меня причинам. Чтение документа для handle_call/3, {noreply, NewState} разрешено также.

Может ли кто-нибудь объяснить это? Чувствует себя очень запутанным для меня.

ИСХОДНЫЙ КОД

-module(server).
-mode(compile).
-behavior(gen_server).

-compile(export_all).

-export([   main/1
          , start/0
          , stop/0
          , stop_and_wait/0
        ]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
        terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 3547).

-record(state, {port, lsock}).

start_link(Port) ->
  gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).

start() ->
  start_link(?DEFAULT_PORT).

stop() ->
  gen_server:cast(?SERVER, stop).

stop_and_wait() ->
  gen_server:call(?SERVER, stop, infinity).

init([Port]) ->
  {ok, LSock} = gen_tcp:listen(Port, [{active, false}, {reuseaddr, true}]),
  {ok, #state{port = Port, lsock = LSock}, 0}.

handle_call({do_stuff, Arg}, _From, State) ->
  io:format("do_stuff is called with ~p~n", [Arg]),
  % {reply, ok, State};
  {noreply, State};

handle_call(stop, _From, State) ->
  {stop, normal, ok, State}.

handle_cast(stop, State) ->
  {stop, normal, State}.

handle_info(timeout, #state{lsock = LSock} = State) ->
  Server = self(),
  Listener = spawn(fun() -> listen(Server, LSock) end),
  {noreply, State}.

terminate(_Reason, _State) ->
  ok.

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

listen(Server, LSock) ->
  {ok, Socket} = gen_tcp:accept(LSock),
  gen_server:call(?SERVER, {do_stuff, 1}),
  listen(Server, LSock).

main(_) ->
  io:format("~p~n", [ok]),
  ok.

person Albert Netymk    schedule 29.12.2015    source источник


Ответы (1)


Возврат {noreply, NewState} из реализации gen_server:handle_call/3 разрешен, но это не означает, что gen_server не должен отвечать на вызов. Скорее, в этом случае предполагается, что gen_server ответит позже, используя вызов gen_server:reply/2.

Время ожидания по умолчанию для gen_server:call/2,3 составляет 5 секунд. В вашем коде происходит то, что ваша функция listen/2 выполняется в процессе, который принимает сокет и, таким образом, является владельцем этого сокета, после чего вызывает gen_server:call(?SERVER, {do_stuff, 1}). Поскольку ваш gen_server не отвечает на этот вызов, gen_server:call истекает через 5 секунд, убивая процесс и, таким образом, закрывая сокет.

person Steve Vinoski    schedule 29.12.2015
comment
Большое тебе спасибо. Я нашел соответствующий документ здесь. - person Albert Netymk; 29.12.2015