简体   繁体   中英

Erlang: How to trigger ECONNRESET on Linux?

I recently encountered heisenbug-like occurrences of ECONNRESET with Erlang on Mac OS X (not my machine!). To simplify the source of the problem, and in order to understand the error itself better, I wrote the following module to trigger ECONNRESET . Unfortunately, I do not own a Mac myself. On my Linux (ArchLinux) the code below does not crash with a badmatch of {error,econnreset} , but with {error,closed} instead.

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

As I encountered ECONNRESET only on Mac OS X yet and not on Linux, this comes down to two questions:

  1. Is the code above even capable of producing ECONNRESET by calling recv/2 ?
  2. Do Mac OS X and Linux behave differently here?

Some scenarios in which this can happen.

Attempting to send new data on a socket, in which the remote endpoint has already started to close it's end of the connection can result in ECONNRESET. (Sometimes it can result in SIGPIPE getting raised).

Normally, this is hard to reproduce because most socket code gets notified of a closed connection by detecting a recv() call returning 0.

It can also happen if the remote host crashes or loses power. A typical scenario is two hosts that have an active TCP connection between each other. Neither host is using TCP keep-alives and only periodically send/recv data between each other. At the moment, assume both hosts are idle and do not have any data to send between each other.

The first host, HOST A, suffers a temporary power outage (ie pull the power cord) and reboots. The other host, HOST B, has no awareness of the outage. As such, the TCP state machine thinks the socket is still in the connected state. At some point, HOST B attempts to send data on the socket. An IP packet eventually reaches HOST A. But the TCP state machine of HOST A doesn't have a connection record for HOST B's IP:port since the reboot. Hence, the only thing it can do is send back a RST to inform the HOST B that the connection is dead.

I've heard, but I could be wrong... that a socket can be forced into a CLOSED state without sending a FIN by using SO_LINGER with a zero value. Hence, subsequent data sent by the other side will result in RST as the response.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM