Erlang接受套接字中的连接仅在存在多个进程层时失败

vktxenjb  于 2022-12-08  发布在  Erlang
关注(0)|答案(1)|浏览(149)

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

-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的另一层时,问题就开始了,创建listen套接字的代码启动了,接受服务器已经产生:

-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),

谢谢你,谢谢你

ejk8hzay

ejk8hzay1#

在不进行测试的情况下,我会说在第二种情况下,作为侦听套接字所有者的进程在生成接受器后自然死亡,从而关闭侦听套接字。
在调用start_servers后添加一些timer:sleepreceive以进行验证。

相关问题