当 handle_call returns 不回复时,erlang otp gen_server 断开连接
erlang otp gen_server drops connection when handle_call returns 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.
允许从 gen_server:handle_call/3
实现返回 {noreply, NewState}
,但这并不意味着 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 秒后超时,终止进程并因此关闭套接字。
使用以下方式启动服务器:
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.
允许从 gen_server:handle_call/3
实现返回 {noreply, NewState}
,但这并不意味着 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 秒后超时,终止进程并因此关闭套接字。