我最近在Mac OS X(不是我的机器!)上用Erlang遇到了类似heisenbug的ECONNRESET
的ECONNRESET
出现。为了简化问题的根源,并且为了更好地理解错误本身,我编写了以下模块来触发{error,econnreset}
。不幸的是,我本人并不拥有Mac。在我的Linux(ArchLinux)上,下面的代码不会因{error,closed}
的不匹配而崩溃,但会导致ECONNRESET
崩溃。
-module(econnreset).
-export([run/0]).
run() ->
Pid = spawn_link(fun listen_and_accept/0),
Pid ! {get_port,self()},
Port = receive {port,P} -> P end,
{ok,Socket} = gen_tcp:connect("localhost", Port, [{active,false}]),
ok = gen_tcp:send(Socket, lists:duplicate(100, $a)),
%% On the following line, I expected a badmatch w/ {error,econnreset}
{ok,_} = gen_tcp:recv(Socket, 0, 1000),
ok = gen_tcp:close(Socket),
ok.
listen_and_accept() ->
{ok,LSocket} = gen_tcp:listen(0, [binary,{active,false}]),
{ok,Port} = inet:port(LSocket),
receive {get_port,Pid} -> Pid ! {port,Port} end,
{ok,Socket} = gen_tcp:accept(LSocket),
{ok,Bin} = gen_tcp:recv(Socket, 1), %=> read only 1 byte to trigger tcp RST
io:format("Server read ~p~n", [Bin]),
gen_tcp:close(Socket),
gen_tcp:close(LSocket).
由于我仅在Mac OS X上而不是Linux上才遇到
ECONNRESET
,这归结为两个问题:recv/2
生成ojit_code吗? 最佳答案
在某些情况下可能会发生这种情况。
尝试在套接字上发送新数据,该套接字中的远程端点已开始关闭它的连接结束,这可能会导致ECONNRESET。 (有时可能会导致SIGPIPE筹集)。
通常,这很难重现,因为大多数套接字代码都是通过检测返回0的recv()调用来通知关闭的连接。
如果远程主机崩溃或断电,也会发生这种情况。典型的场景是两台主机之间相互之间具有 Activity 的TCP连接。两个主机都没有使用TCP保持 Activity ,并且仅在彼此之间定期发送/接收数据。目前,假设两个主机都处于空闲状态,并且彼此之间没有任何数据可发送。
第一台主机HOST A暂时停电(即拔下电源线)并重新启动。另一台主机HOST B没有发生中断的情况。这样,TCP状态机认为套接字仍处于连接状态。在某个时候,主机B尝试在套接字上发送数据。 IP数据包最终到达主机A。但是,自重启以来,主机A的TCP状态机没有主机B的IP:端口的连接记录。因此,它唯一能做的就是发回RST通知HOST B,该连接已死。
我听说过,但是我可能错了……通过使用具有零值的SO_LINGER可以将套接字强制为CLOSED状态而无需发送FIN。因此,另一端发送的后续数据将以RST作为响应。