在 Erlang 中通过 RPC 在远程节点上创建套接字时无法接受套接字上的连接
Unable to accept connections on socket, when creating sockets on remote node via RPC in Erlang
我正在努力找出 gen_tcp:accept 总是返回 {error, closed} 响应的原因。
本质上,我有一个创建监听套接字的主管:
gen_tcp:listen(8081, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
然后将此套接字传递给 child,这是 gen_server 行为的实现。 child 然后接受套接字上的连接。
accept(ListeningSocket, {ok, Socket}) ->
spawn(fun() -> loop(Socket) end),
accept(ListeningSocket);
accept(_ListeningSocket, {error, Error}) ->
io:format("Unable to listen on socket: ~p.~n", [Error]),
gen_server:call(self(), stop).
accept(ListeningSocket) ->
accept(ListeningSocket, gen_tcp:accept(ListeningSocket)).
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("~p~n", [Data]),
process_request(Data),
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} -> ok
end.
我在本地加载主管和 gen_server BEAM 二进制文件,并通过对 code:load_binary 的 RPC 调用将它们加载到另一个节点(运行 在同一台机器上)。
接下来,我通过 RPC 调用执行主管,这又会启动服务器。{error, closed} 在这种情况下总是由 gen_tcp:accept 返回。
如果我 运行 主管和服务器登录到节点 shell,那么服务器可以毫无问题地接受连接。这包括 'remsh' 到将无法接受连接的远程节点,如果我之前通过 RPC 将其启动服务器失败。
我似乎能够通过单独使用 shell 来重现该问题:
[Terminal 1]: erl -sname node -setcookie abc -distributed -noshell
[Terminal 2]: erl -sname rpc -setcookie abc:
net_adm:ping('node@verne').
{ok, ListeningSocket} = rpc:call('node@verne', gen_tcp, listen, [8081, [binary, {packet, 0}, {active, true}, {reuseaddr, true}]]).
rpc:call('node@verne', gen_tcp, accept, [ListeningSocket]).
对最终 RPC 的响应是{error, closed}。
这可能与 socket/port 所有权有关吗?
如果有帮助,没有客户端等待连接,我也没有在任何地方设置超时。
每个rpc:call
在目标节点上启动一个新进程来处理请求。在您的最后一个示例中,您的第一个调用在这样的进程中创建了一个监听套接字,当该进程在 rpc 调用结束时死亡时,套接字将关闭。由于 already-closed 侦听套接字,您尝试接受的第二次 rpc 调用因此失败。
您的设计在几个方面看起来很不寻常。例如,主管打开套接字是不正常的。您还说 child 是一个 gen_server
但您显示了一个手动 recv
循环,如果 gen_server
中的 运行 会阻止它。相反,您可以解释您要完成的任务,并请求帮助设计出满足您目标的设计。
我正在努力找出 gen_tcp:accept 总是返回 {error, closed} 响应的原因。
本质上,我有一个创建监听套接字的主管:
gen_tcp:listen(8081, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
然后将此套接字传递给 child,这是 gen_server 行为的实现。 child 然后接受套接字上的连接。
accept(ListeningSocket, {ok, Socket}) ->
spawn(fun() -> loop(Socket) end),
accept(ListeningSocket);
accept(_ListeningSocket, {error, Error}) ->
io:format("Unable to listen on socket: ~p.~n", [Error]),
gen_server:call(self(), stop).
accept(ListeningSocket) ->
accept(ListeningSocket, gen_tcp:accept(ListeningSocket)).
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("~p~n", [Data]),
process_request(Data),
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} -> ok
end.
我在本地加载主管和 gen_server BEAM 二进制文件,并通过对 code:load_binary 的 RPC 调用将它们加载到另一个节点(运行 在同一台机器上)。 接下来,我通过 RPC 调用执行主管,这又会启动服务器。{error, closed} 在这种情况下总是由 gen_tcp:accept 返回。
如果我 运行 主管和服务器登录到节点 shell,那么服务器可以毫无问题地接受连接。这包括 'remsh' 到将无法接受连接的远程节点,如果我之前通过 RPC 将其启动服务器失败。
我似乎能够通过单独使用 shell 来重现该问题:
[Terminal 1]: erl -sname node -setcookie abc -distributed -noshell
[Terminal 2]: erl -sname rpc -setcookie abc:
net_adm:ping('node@verne').
{ok, ListeningSocket} = rpc:call('node@verne', gen_tcp, listen, [8081, [binary, {packet, 0}, {active, true}, {reuseaddr, true}]]).
rpc:call('node@verne', gen_tcp, accept, [ListeningSocket]).
对最终 RPC 的响应是{error, closed}。
这可能与 socket/port 所有权有关吗?
如果有帮助,没有客户端等待连接,我也没有在任何地方设置超时。
每个rpc:call
在目标节点上启动一个新进程来处理请求。在您的最后一个示例中,您的第一个调用在这样的进程中创建了一个监听套接字,当该进程在 rpc 调用结束时死亡时,套接字将关闭。由于 already-closed 侦听套接字,您尝试接受的第二次 rpc 调用因此失败。
您的设计在几个方面看起来很不寻常。例如,主管打开套接字是不正常的。您还说 child 是一个 gen_server
但您显示了一个手动 recv
循环,如果 gen_server
中的 运行 会阻止它。相反,您可以解释您要完成的任务,并请求帮助设计出满足您目标的设计。