Erlang在socket中接受连接只有在有多层进程时才会失败

Posted

技术标签:

【中文标题】Erlang在socket中接受连接只有在有多层进程时才会失败【英文标题】:Erlang accepting connection in socket fails only when there are multiple layers of processes 【发布时间】:2022-01-03 07:31:39 【问题描述】:

我遇到了一个奇怪的问题。我正在测试docs 中的代码,它创建了几个进程来接受来自监听套接字的传入连接:

-module(servertest).

%% API
-export([start/2, server/1]).

start(Num,LPort) ->
  case gen_tcp:listen(LPort,[active, false,packet,0]) of
    ok, ListenSock ->
      start_servers(Num,ListenSock),
      ok, Port = inet:port(ListenSock),
      Port;
    error,Reason ->
      error,Reason
  end.

start_servers(0,_) ->
  ok;
start_servers(Num,LS) ->
  spawn(?MODULE,server,[LS]),
  start_servers(Num-1,LS).

server(LS) ->
  io:format("server started~n", []),
  case gen_tcp:accept(LS) of
    ok,S ->
      loop(S),
      server(LS);
    Other ->
      io:format("accept returned ~w - goodbye!~n",[Other]),
      ok
  end.

loop(S) ->
  inet:setopts(S,[active,once]),
  receive
    tcp,S,Data ->
      Answer = Data, % Not implemented in this example
      gen_tcp:send(S,Answer),
      loop(S);
    tcp_closed,S ->
      io:format("Socket ~w closed [~w]~n",[S,self()]),
      ok
  end.

这很好用:

servertest:start(1, 1241).
server started
1241

当我添加另一层spawn 并且创建侦听套接字的代码启动接受服务器时,问题就开始了:

-module(servertest2).

%% API
-export([start/2, server/1, start_listening/2]).

start(Num,LPort) ->
  erlang:spawn_link(?MODULE, start_listening, [Num,LPort]).

start_listening(Num,LPort) ->
  case gen_tcp:listen(LPort,[active, false,packet,0]) of
    ok, ListenSock ->
      start_servers(Num,ListenSock),
      ok, Port = inet:port(ListenSock),
      Port;
    error,Reason ->
      error,Reason
  end.

start_servers(0,_) ->
  ok;
start_servers(Num,LS) ->
  spawn(?MODULE,server,[LS]),
  start_servers(Num-1,LS).

server(LS) ->
  io:format("server started~n", []),
  case gen_tcp:accept(LS) of
    ok,S ->
      loop(S),
      server(LS);
    Other ->
      io:format("accept returned ~w - goodbye!~n",[Other]),
      ok
  end.

loop(S) ->
  inet:setopts(S,[active,once]),
  receive
    tcp,S,Data ->
      Answer = Data, % Not implemented in this example
      gen_tcp:send(S,Answer),
      loop(S);
    tcp_closed,S ->
      io:format("Socket ~w closed [~w]~n",[S,self()]),
      ok
  end.

当它像这样启动时,accept 立即(没有任何传入连接)返回error,closed

servertest2:start(1, 1242).
server started
<0.13452.0>
accept returned error,closed - goodbye!

为了澄清这两个版本之间的差异:

--- servertest.erl      2021-11-25 00:04:32.000000000 +0100
+++ servertest2.erl     2021-11-25 00:04:01.000000000 +0100
@@ -1,9 +1,12 @@
--module(servertest).
+-module(servertest2).
 
 %% API
--export([start/2, server/1]).
+-export([start/2, server/1, start_listening/2]).
 
 start(Num,LPort) ->
+  erlang:spawn_link(?MODULE, start_listening, [Num,LPort]).
+
+start_listening(Num,LPort) ->
   case gen_tcp:listen(LPort,[active, false,packet,0]) of
     ok, ListenSock ->
       start_servers(Num,ListenSock),

谢谢!

【问题讨论】:

【参考方案1】:

如果不对其进行测试,我会说在第二种情况下,作为监听套接字所有者的进程在生成接受器后自然死亡,从而关闭监听套接字。

在调用start_servers验证后添加一些timer:sleepreceive

【讨论】:

第一种情况不也是这样吗? @gphilip 不,因为在第一个监听套接字的进程所有者是 erlang shell

以上是关于Erlang在socket中接受连接只有在有多层进程时才会失败的主要内容,如果未能解决你的问题,请参考以下文章

生成后,Tcp Socket 在 erlang 中的远程节点上不起作用,错误,关闭?

tcp客户端socket

在 Erlang 中通过 RPC 在远程节点上创建套接字时,无法接受套接字上的连接

Erlang 建立TCP连接后,怎么样从返回的Socket获取客户端ip和端口

Erlang 中的接受器池和负载平衡?

在Python中,当在localhost上接受TCP套接字连接时,无法获得正确的对等体IP地址。