Erlang新增进程层后监听套接字accept连接立即返回closed错误
问题原因
Erlang的TCP套接字(包括监听套接字)默认归创建它的进程所有,一旦所属进程退出,系统会自动关闭该套接字,这是问题的核心诱因:
- 修改后的代码中,
start_listening/2函数是在独立spawn的进程中执行的,它完成「创建监听套接字、启动accept服务进程、获取端口号」这一系列逻辑后,函数执行结束,该进程就直接正常退出了 - 进程退出后,它创建的监听套接字被系统自动关闭,此时已经启动的accept进程调用
gen_tcp:accept(LS)时,操作的是已经关闭的监听套接字,就会立即返回{error,closed}错误
解决方案
有三个常用的修复方案,按需选择即可:
方案1:让监听套接字的创建进程保持存活
修改start_listening/2函数,在逻辑执行完成后让进程阻塞挂住不退出,即可维持监听套接字存活,修改示例:
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), %% 新增阻塞逻辑,进程永远挂住不退出,也可以自定义停止信号实现服务关停 receive stop -> ok end; {error,Reason} -> {error,Reason} end.
方案2:创建监听套接字的进程自身承担accept逻辑
把创建监听套接字的进程也作为accept工作进程使用,自然不会提前退出,修改示例:
start_listening(Num,LPort) -> case gen_tcp:listen(LPort,[{active, false},{packet,0}]) of {ok, ListenSock} -> %% 少启动1个工作进程,当前进程自己承担accept任务 start_servers(Num-1,ListenSock), {ok, Port} = inet:port(ListenSock), %% 直接进入accept循环 server(ListenSock); {error,Reason} -> {error,Reason} end.
方案3:转移监听套接字的控制进程
在创建监听套接字后,将它的控制进程转移给任意一个不会退出的进程(比如第一个启动的accept工作进程),转移后原创建进程退出就不会关闭套接字了,转移调用语法:
%% 把ListenSock的控制进程从当前进程改为目标进程TargetPid inet:controlling_process(ListenSock, TargetPid)
内容的提问来源于stack exchange,提问作者gphilip
相关产品推荐
相关产品推荐

