TCPSocket doesn't detect remote disconnection inmediatelly

Discussion in 'Ruby' started by Iñaki Baz Castillo, Jan 12, 2010.

  1. Hi, I'm experimenting an annoying issue when a server disconnects a TCP=20
    connection established by a Ruby client.

    Let me start with an example of what I consider the expected behavior:


    a) Using Linux "telnet" command:

    =2D First connect to google.com:

    ~$ telnet google.com 80 =
    =20

    Trying 209.85.229.106... =
    =20
    Connected to google.com. =
    =20
    Escape character is '^]'.

    =2D Then write something ("hello\n"):

    hello

    =2D So I receive a 400 response:

    HTTP/1.0 400 Bad Request
    [...]

    =2D And a warning:

    "Connection closed by foreign host."

    =2D telnet exits with status 1 due to the remote disconnection error.



    b) Using Ruby 1.9.1 TCPSocket:

    =2D First connect:

    irb> require "socket"
    true
    irb> socket =3D TCPSocket.new("google.com", 80)
    #<TCPSocket:0x00000001010288>

    =2D Now netstat shows the connection as ESTABLISHED (ok).

    =2D Write "hello\n" into the socket:

    irb> socket.puts "hello"
    nil

    =2D I receive the same 400 response from the www server. Now netstat shows =
    the=20
    connection as CLOSE_WAIT (OK?).

    =2D Write "hello\n" again (IMHO it should fail):

    irb> socket.puts "hello"
    nil <---- didn't raise !!!

    =2D Doing a ngrep I see that irb has sent "hello\n" over the previous TCP=20
    connection but this time it doesn't receive response from the www server. T=
    his=20
    is, irb is using a closed connection!:

    T 2010/01/12 20:12:04.072705 192.168.1.10:35272 -> 209.85.229.105:80 [AP]
    hello
    #

    =2D But after this new "puts" now netstat shows nothing! (no connection =3D=
    > OK).


    =2D Repeat again:

    irb> socket.puts "hello"
    Errno::EPIPE: Broken pipe
    from (irb):6:in `write'
    from (irb):6:in `puts'
    from (irb):6
    from /usr/bin/irb:12:in `<main>'

    =2D Now it raises!


    Why didn't Ruby raise in the second "puts" attemp??? the server already clo=
    sed=20
    the connection! Why does Ruby realize of it after a new retry?

    Well, this issue causes one request lost (the second one) since there is no=
    =20
    way to know if the server accepted or dropped the request.

    Is it a bug? is there a way to check the socket connection before sending=20
    (loosing in fact) a request? (TCPSocket#closed? is not valid here as it jus=
    t=20
    returns true in case *we* have closed a socket).

    Thanks a lot for any help.



    =2D-=20
    I=C3=B1aki Baz Castillo <>
     
    Iñaki Baz Castillo, Jan 12, 2010
    #1
    1. Advertising

  2. Iñaki Baz Castillo wrote:
    > Hi, I'm experimenting an annoying issue when a server disconnects a TCP
    > connection established by a Ruby client.


    Try writing a C program which uses the Unix socket API directly. It will
    be instructive. The book "Unix Network Programming (vol 1)" by the late
    Richard Stevens is an excellent authoritative tome.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Jan 12, 2010
    #2
    1. Advertising

  3. El Martes, 12 de Enero de 2010, Brian Candler escribi=C3=B3:
    > I=C3=B1aki Baz Castillo wrote:
    > > Hi, I'm experimenting an annoying issue when a server disconnects a TCP
    > > connection established by a Ruby client.

    >=20
    > Try writing a C program which uses the Unix socket API directly. It will
    > be instructive. The book "Unix Network Programming (vol 1)" by the late
    > Richard Stevens is an excellent authoritative tome.


    I'll do it. But it would be great if at least I could know in advance if wh=
    at=20
    I described is the expected behavior :)


    =2D-=20
    I=C3=B1aki Baz Castillo <>
     
    Iñaki Baz Castillo, Jan 12, 2010
    #3
  4. El Martes, 12 de Enero de 2010, I=C3=B1aki Baz Castillo escribi=C3=B3:
    > El Martes, 12 de Enero de 2010, Brian Candler escribi=C3=B3:
    > > I=C3=B1aki Baz Castillo wrote:
    > > > Hi, I'm experimenting an annoying issue when a server disconnects a T=

    CP
    > > > connection established by a Ruby client.

    > >
    > > Try writing a C program which uses the Unix socket API directly. It will
    > > be instructive. The book "Unix Network Programming (vol 1)" by the late
    > > Richard Stevens is an excellent authoritative tome.

    >=20
    > I'll do it. But it would be great if at least I could know in advance if
    > what I described is the expected behavior :)


    Well, I've not tested in C yet, but I confirm that in PHP the same occurs :)

    =20


    =2D-=20
    I=C3=B1aki Baz Castillo <>
     
    Iñaki Baz Castillo, Jan 12, 2010
    #4
  5. Iñaki Baz Castillo

    Aaron Turner Guest

    On Tue, Jan 12, 2010 at 1:21 PM, I=F1aki Baz Castillo <> wrote=
    :
    > El Martes, 12 de Enero de 2010, Brian Candler escribi=F3:
    >> I=F1aki Baz Castillo wrote:
    >> > Hi, I'm experimenting an annoying issue when a server disconnects a TC=

    P
    >> > connection established by a Ruby client.

    >>
    >> Try writing a C program which uses the Unix socket API directly. It will
    >> be instructive. The book "Unix Network Programming (vol 1)" by the late
    >> Richard Stevens is an excellent authoritative tome.

    >
    > I'll do it. But it would be great if at least I could know in advance if =

    what
    > I described is the expected behavior :)


    Long story short, what you're seeing with Ruby is not a bug, but
    rather how TCP sockets work. TCP sockets are bi-directional and when
    one side "closes" the connection, they are not actually causing the
    TCP session to tear down as telnet implies, but rather is informing
    the other end of the connection that they have no more data to send.
    The receive direction is still technically open via TCP, although the
    HTTP protocol specifies no more communication is possible.

    I also recommend "Unix Network Programming (vol 1)" by Stevens if you
    want to understand the details of sockets and network communication.
    Since network programming utilizes much of the same API constructs as
    file IO, many newbie network programmers make these sorta mistakes
    because they expect network sockets to behave like file descriptors
    when they do not.

    --=20
    Aaron Turner
    http://synfin.net/
    http://tcpreplay.synfin.net/ - Pcap editing and replay tools for Unix & Win=
    dows
    Those who would give up essential Liberty, to purchase a little temporary
    Safety, deserve neither Liberty nor Safety.
    -- Benjamin Franklin
    "carpe diem quam minimum credula postero"
     
    Aaron Turner, Jan 12, 2010
    #5
  6. El Martes, 12 de Enero de 2010, Aaron Turner escribi=F3:
    > Long story short, what you're seeing with Ruby is not a bug, but
    > rather how TCP sockets work. TCP sockets are bi-directional and when
    > one side "closes" the connection, they are not actually causing the
    > TCP session to tear down as telnet implies, but rather is informing
    > the other end of the connection that they have no more data to send.
    > The receive direction is still technically open via TCP, although the
    > HTTP protocol specifies no more communication is possible.


    Understood, thanks a lot.

    Then I wonder if there is some way to detect that the remote has closed its=
    =20
    side. When it occurs the remote sends a TCP segment with ACK+FIN flags=20
    enabled.

    Is there a way to detect it in Ruby using TCPSocket? if there some attribut=
    e=20
    of the socket object that changes whe such ACK+FIN arrives? (however I've=20
    tried most of the TCPSocket methods and found nothing).

    The underlaying system of courses knows it as in case a new TCP segment is=
    =20
    sent (by the Ruby client) it's sent with big PUSH enabled, knowing that the=
    =20
    remote will reply with RESET flag enabled. Is the Ruby TCPSocket aware that=
    =20
    its message is sent with PUSH flag enabled? if so it could raise something=
    =20
    (optionally) so I know that I'll get no response and could reconnect and=20
    retry. Is it possible?

    Humm, I think that all this stuff is done at kernel TCP layer so the socket=
    =20
    user has nothing to deal with... am I right?


    > I also recommend "Unix Network Programming (vol 1)" by Stevens if you
    > want to understand the details of sockets and network communication.
    > Since network programming utilizes much of the same API constructs as
    > file IO, many newbie network programmers make these sorta mistakes
    > because they expect network sockets to behave like file descriptors
    > when they do not.


    Sure. Thanks a lot.



    =2D-=20
    I=F1aki Baz Castillo <>
     
    Iñaki Baz Castillo, Jan 12, 2010
    #6
  7. Iñaki Baz Castillo

    Aaron Turner Guest

    On Tue, Jan 12, 2010 at 2:37 PM, I=F1aki Baz Castillo <> wrote=
    :
    > El Martes, 12 de Enero de 2010, Aaron Turner escribi=F3:
    >> Long story short, what you're seeing with Ruby is not a bug, but
    >> rather how TCP sockets work. =A0TCP sockets are bi-directional and when
    >> one side "closes" the connection, they are not actually causing the
    >> TCP session to tear down as telnet implies, but rather is informing
    >> the other end of the connection that they have no more data to send.
    >> The receive direction is still technically open via TCP, although the
    >> HTTP protocol specifies no more communication is possible.

    >
    > Understood, thanks a lot.
    >
    > Then I wonder if there is some way to detect that the remote has closed i=

    ts
    > side. When it occurs the remote sends a TCP segment with ACK+FIN flags
    > enabled.
    >
    > Is there a way to detect it in Ruby using TCPSocket? if there some attrib=

    ute
    > of the socket object that changes whe such ACK+FIN arrives? (however I've
    > tried most of the TCPSocket methods and found nothing).


    There are two ways of doing this:

    1) The application running over TCP. In this case the HTTP 400 error
    is telling you that the web server is done talking to you and no
    future requests will be processed.

    2) The other way is when you read, did you get an EOF error? EOF =3D=3D
    server has called shutdown() on the socket.

    Ruby sockets behave just like C sockets. It's just the Ruby language
    wrapping them. There's nothing "special" about ruby sockets. What
    this means is that the best resource isn't the ruby docs- they just
    document the ruby API. To understand how sockets work, read Richard
    Stevens or start doing google searches on things like "tcp detecting
    close".

    > The underlaying system of courses knows it as in case a new TCP segment i=

    s
    > sent (by the Ruby client) it's sent with big PUSH enabled, knowing that t=

    he
    > remote will reply with RESET flag enabled. Is the Ruby TCPSocket aware th=

    at
    > its message is sent with PUSH flag enabled? if so it could raise somethin=

    g
    > (optionally) so I know that I'll get no response and could reconnect and
    > retry. Is it possible?


    The PSH (Push) flag has nothing to do with it. PSH just tells the
    receiving host that the TCP packet has interesting data for the
    application and it should quickly "push it up" to the application for
    processing.

    > Humm, I think that all this stuff is done at kernel TCP layer so the sock=

    et
    > user has nothing to deal with... am I right?


    Depends on what you mean by "all this stuff". If you mean making TCP
    a reliable, bi-directional stream & session based programming
    construct then yes, the kernel does that for you. If you mean dealing
    with knowing when you can read/write to the socket, then no, that's
    your job. You the programmer have a lot of control over tcp sockets
    and are expected to know how to use them properly. As I said last
    time, while TCP sockets utilize a very similar API as File IO, but
    they are more complicated and you need to know how to use them.

    Good luck.

    --=20
    Aaron Turner
    http://synfin.net/
    http://tcpreplay.synfin.net/ - Pcap editing and replay tools for Unix & Win=
    dows
    Those who would give up essential Liberty, to purchase a little temporary
    Safety, deserve neither Liberty nor Safety.
    -- Benjamin Franklin
    "carpe diem quam minimum credula postero"
     
    Aaron Turner, Jan 12, 2010
    #7
  8. Re: TCPSocket doesn't detect remote disconnection inmediatelly[SOLVED]

    El Mi=E9rcoles, 13 de Enero de 2010, Aaron Turner escribi=F3:

    > > Is there a way to detect it in Ruby using TCPSocket? if there some
    > > attribute of the socket object that changes whe such ACK+FIN arrives?
    > > (however I've tried most of the TCPSocket methods and found nothing).

    >=20
    > There are two ways of doing this:
    >=20
    > 1) The application running over TCP. In this case the HTTP 400 error
    > is telling you that the web server is done talking to you and no
    > future requests will be processed.


    I'm not talking with a HTTP server, but with a SIP server. When the SIP ser=
    ver=20
    restarts its sends a TCP packet with FIN flag to me (Ruby client).

    =20
    > 2) The other way is when you read, did you get an EOF error? EOF =3D=3D
    > server has called shutdown() on the socket.


    I have good news for myself :)

    I've a thread reading the responses received in the socket:

    sip_response =3D @@socket.readline("\r\n\r\n")

    I use "\r\n\r\n" (double CRLF) as a SIP response with no body ends with CRL=
    =46=20
    CRLF (as HTTP I think).

    However when the remote disconnects I didn't get the exception EOFError.

    The very good new is that it's a bug in my code! In the rescue block I did =
    a=20
    typo so I wasn't invoking the reconnection command :)


    Really thanks for the good help received in this thread :)


    =2D-=20
    I=F1aki Baz Castillo <>
     
    Iñaki Baz Castillo, Jan 12, 2010
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Avizz
    Replies:
    3
    Views:
    13,969
    Andy Fish
    Sep 29, 2003
  2. Jason Teagle
    Replies:
    12
    Views:
    3,355
    Jason Teagle
    Oct 5, 2003
  3. DennyOR
    Replies:
    10
    Views:
    1,173
    Chris Uppal
    May 13, 2005
  4. Brandon McCombs

    disconnection on exit

    Brandon McCombs, Feb 6, 2006, in forum: Java
    Replies:
    3
    Views:
    363
    Brandon McCombs
    Feb 6, 2006
  5. Andrew Edwards
    Replies:
    3
    Views:
    112
    Brian Candler
    May 20, 2009
Loading...

Share This Page