[erlang-questions] {error,closed} vs. {error,econnreset}

Bekes, Andras G <>
Tue May 5 09:44:47 CEST 2015


Thank you very much for your efforts Rory.

The ability "to set a socket option that shows all econnreset errors" sounds like the right solution. I am wondering why hiding this detail is the default, but I believe there were good enough reasons to design it that way.

I accept that your solution will not notice the connection reset event in some corner cases. I think this will not apply in my case: I am sending a small amount of data (<1KB) and wait for the reply.

I am looking forward to see your patch in the next release of Erlang/OTP!

Thanks,
   Andras

-----Original Message-----
From:  [mailto:] On Behalf Of Rory Byrne
Sent: Saturday, May 02, 2015 12:32 PM
To: 
Subject: Re: [erlang-questions] {error,closed} vs. {error,econnreset}

Hi Andras,

On Fri, May 01, 2015 at 11:53:13AM +0000, Bekes, Andras G wrote:
> 
> Please see the trace in the attached file. Do you have suggestions on what else shall I trace?
> 
>>> On 2015-02-10 14:39, Bekes, Andras G wrote:
>>> 
>>> Looks like I have a problem with the erroneous result of gen_tcp:recv/2, as it returns {error,closed} instead of my expectation of {error,econnreset}.
>>> 
...
>>> 
>>> Unfortunately it looks like I really need to separate these two results.
>>> 
>>> I also tried gen_tcp in active mode, but no difference, the result is {error,closed} instead of {error,econnreset}.
>>> 
>>> Can someone explain why the econnreset error is masked? Is there any way I can separate the two kinds of events?
>>> 

It seems it was a design decision to mask both ECONNRESET recv errors and
all send errors. You can see where this happens for recv errors at around
line 9988 in erts/emulator/drivers/common/inet_drv.c. Open that file and
search for ECONNRESET and you will find the following section of code.

    n = sock_recv(desc->inet.s, desc->i_ptr, nread, 0);

    if (IS_SOCKET_ERROR(n)) {
        int err = sock_errno();
        if (err == ECONNRESET) {
            DEBUGF((" => detected close (connreset)\r\n"));
            return tcp_recv_closed(desc);
        }
        if (err == ERRNO_BLOCK) {
            DEBUGF((" => would block\r\n"));
            return 0;
        }
        else {
            DEBUGF((" => error: %d\r\n", err));
            return tcp_recv_error(desc, err);
        }
    }
    else if (n == 0) {
        DEBUGF(("  => detected close\r\n"));
        return tcp_recv_closed(desc);
    }

As you can see from this, ECONNRESET is being treated as though it's an EOF 
rather than an error: both end up calling tcp_recv_closed(). As a quick fix to
your problem, change the code to read:

        if (err == ECONNRESET) {
            DEBUGF((" => detected close (connreset)\r\n"));
            return tcp_recv_error(desc, err);
        }

However, this is an indiscriminate change which will effect all socket code,
including the networking code for distributed Erlang, and any third party
Erlang code you are using.

Also, be warned that this fix won't alert you to every incoming RST. For
example, if you have a large payload buffered in the socket driver in Erlang 
(i.e. a payload that is too large for the kernel socket send buffer) and you 
receive an RST, then your next call to gen_tcp:recv/2 will return 
{error, closed} rather than {error, econnreset}. See below for example code 
which shows this. The reason this happens is as follows:

 1. When there is outbound data buffered in the socket driver queue, 
    gen_tcp:send/2 becomes an asynchronous call. Which is to say, the new data
    is added to the driver queue and the call returns 'ok'. Then the actual 
    send syscall takes place at a later time, according to epoll, select or 
    whatever.

 2. Then, when you are in passive mode and an error is detected on the send 
    syscall there is no caller to return it to, so it is marked as a 
    "delayed close error". This has two consequences: 

      (a) it is masked to a generic {error, closed}; and
      (b) it is returned on the next call to gen_tcp:recv/2 or gen_tcp:send/2.

So, the send error is ultimately returned on a gen_tcp:recv/2 call, and all
send errors are masked as {error, closed}.

In active mode the problems are similar.

I've got a patch for the recv errors and I'm working on a patch for the send 
errors. Both patches will allow the user to set a socket option that shows all 
econnreset errors (changing some epipe errors to econnreset in the case of send
errors). With any luck, I'll release these patches over the next week or
two as part of a larger set of patches. No guarantee they'll be accepted 
though.

Rory

%%--- Server Code ---
-module(server).
-export([run/0]).

-define(PORT, 7777).
-define(RECBUF, (4 * 1024)).

run() ->
    SockOpts = [{recbuf, ?RECBUF}, {reuseaddr, true}, {active, false}],
    {ok, LSock} = gen_tcp:listen(?PORT, SockOpts),
    {ok, Sock} = gen_tcp:accept(LSock),
    ok = gen_tcp:close(LSock),
    ok = inet:setopts(Sock, [{linger, {true, 0}}]),
    ok = gen_tcp:send(Sock, "Any payload"),
    timer:sleep(1000),
    ok = gen_tcp:close(Sock).

%%--- Client Code ---
-module(client).
-export([run/0]).

-define(PORT, 7777).
-define(SNDBUF, (4 * 1024)).
-define(PAYLOAD_SIZE, (30 * 1024)).

run() ->
    SockOpts = [{sndbuf, ?SNDBUF}, {active, false}],
    {ok, Sock} = gen_tcp:connect("localhost", ?PORT, SockOpts),
    Payload = lists:duplicate(?PAYLOAD_SIZE, $.),
    ok = gen_tcp:send(Sock, Payload),
    ok = timer:sleep(2000),
    Res = gen_tcp:recv(Sock, 0),
    io:format("Result: ~p~n", [Res]),
    gen_tcp:close(Sock).
_______________________________________________
erlang-questions mailing list

http://erlang.org/mailman/listinfo/erlang-questions


--------------------------------------------------------------------------------

NOTICE: Morgan Stanley is not acting as a municipal advisor and the opinions or views contained herein are not intended to be, and do not constitute, advice within the meaning of Section 975 of the Dodd-Frank Wall Street Reform and Consumer Protection Act. If you have received this communication in error, please destroy all electronic and paper copies; do not disclose, use or act upon the information; and notify the sender immediately. Mistransmission is not intended to waive confidentiality or privilege. Morgan Stanley reserves the right, to the extent permitted under applicable law, to monitor electronic communications. This message is subject to terms available at the following link: http://www.morganstanley.com/disclaimers. If you cannot access these links, please notify us by reply message and we will send the contents to you. By messaging with Morgan Stanley you consent to the foregoing.


More information about the erlang-questions mailing list