我最近在Mac OS X(不是我的机器!)上用Erlang遇到了类似heisenbug的ECONNRESETECONNRESET出现。为了简化问题的根源,并且为了更好地理解错误本身,我编写了以下模块来触发{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吗?
  • Mac OS X和Linux的行为是否有所不同?
  • 最佳答案

    在某些情况下可能会发生这种情况。

    尝试在套接字上发送新数据,该套接字中的远程端点已开始关闭它的连接结束,这可能会导致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作为响应。

    10-08 11:41