#45 econnreset error => subclass of eof

Sam Steingold
Don Cohen

Right now, when a socket is reset by the peer, nothing exciting happens until you try to read past the last available input.
At that point you get a OS error (ECONNRESET) which is not an EOF condition (I think it should be), and this further closes the fd, without
marking the stream as closed (which I think it should not do). The result is that further attempts to use the stream give EBADF OS errors.
- new condition class CONNECTION-RESET:
(define-condition connection-reset (end-of-file) ())
- change in CLISP behavior s.t. ECONNRESET results in CONNECTION-RESET
and not in OS-ERROR (or whatever)
- don't close fd on ECONNRESET - it should act like other eof's - another attempt to read from the stream should cause another eof.
Test code (at least for linux)
This requires two lisp processes, one for the client, one for the server. I make use of the fact that closing a socket when there is
still input available seems to result (again, only tested in linux) in a TCP reset rather than a FIN. The result of a FIN terminated
connection is what I view as correct, so I will demonstrate that also.
[server] (setf ss (socket:socket-server 1234))
[server] (setf s (socket-accept ss))
[client] (setf s (socket:socket-connect 1234))
[server-RST] (princ "asd" s)
[client] (close s)
at this point the stream is closed, either with FIN if the [server-RST] line is left out, or with RST if it is included
[server] (read-char s)
the result in FIN scenario is an EOF error, in the RST scenario it's *** - UNIX error 9 (EBADF): Bad file number
[server] (read-char s)
in the FIN scenario the result is another EOF error, in the RST scenario it's *** - UNIX error 9 (EBADF): Bad file number
In both cases s appears to be an open stream, though in the RST scenario, the underlying fd has been closed.


<< < 1 2 (Page 2 of 2)
  • Don Cohen
    Don Cohen

    A little new info on this:
    The first reply in comp.lang.lisp mentions something I also saw
    searching comp.protocols.tcp-ip. It's in rfc1122:

    A host MAY implement a "half-duplex" TCP close sequence, so
    that an application that has called CLOSE cannot continue to
    read data from the connection. If such a host issues a
    CLOSE call while received data is still pending in TCP, or
    if new data is received after CLOSE is called, its TCP
    SHOULD send a RST to show that data was lost.

    This at least explains the behavior that I thought was not justified
    by rfc 793.
    Just to clarify, if the client sends a FIN, that means that he is
    done sending but is still reading.
    This form of reset is a signal that the peer is not listening any
    more. It seems more relevant to writing than reading. It's really a
    signal that your write is not working.

    Note that the tcp is actually connected to some other program and even
    if tcp has delivered all the data to that program, it cannot tell
    whether that program ever looked at that data. Watch this:

    [server] (setf s (socket-accept ss))
    [client] (setf s (socket:socket-connect 1234))
    [server] (princ "asd" s)
    up to here the same script we saw before
    [client](read-char s)
    [client] (close s)
    The previous script skipped the read-char.
    In that case we get RST.
    By doing the read-char, clisp retrieves the data in the tcp queue,
    namely "asd". Tcp considers that data to be delivered, even though
    the lisp application has only looked at the "a". In this case the
    close ends with FIN. And therefore,
    [server] (read-char s)
    *** - READ: input stream #<IO INPUT-BUFFERED SOCKET-STREAM CHARACTER> has reached its end

  • Don Cohen
    Don Cohen

    Now let me respond to your poll.
    Your question was slightly misleading.
    I'd have preferred something that specifically says
    when you try to read on a stream that has been reset and you get to
    the end, should you get a signal that is a (subclass of) EOF
    In this context, the definition of EOF makes my case:
    The type end-of-file consists of error conditions related to read
    operations that are done on streams that have no more data.

    On the other hand I thought your example was good.
    The point is that when the peer closes the connection then your
    example code *MAY* get an error (previous post shows both cases).

    The problem I see is that you can't really tell whether the RST
    is a signal that your output was not all read or something more
    serious (the peer went down and then came back up with no record
    of this connection). (This is what I don't like about rfc1122.)
    I don't see that failure to read all of the input indicates an
    error, and as I pointed out, this RST is not a good indication
    of that anyway.

    Therefore, as a programmer you have to make a choice.
    The problem is that it's so inconvenient to make the choice now.
    I think this justifies some support for controlling the choice, as
    I suggested in the post of 2011-04-11 18:34:10 GMT.

<< < 1 2 (Page 2 of 2)