Socket issue

Discussion in 'Ruby' started by Marc Soda, Jan 22, 2007.

  1. Marc Soda

    Marc Soda Guest

    Hey all,

    While testing some edge cases in an app I came across a strange socket
    issue. If I have a server that dies unexpectedly, and then try to
    write to it from an already connected client, it doesn't raise an
    exception until the next time I try to write it.

    Example:

    Server
    ---------
    require 'socket'

    include Socket::Constants

    sock = Socket.new(AF_INET, SOCK_STREAM, 0)
    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, true)
    sockaddr = Socket.pack_sockaddr_in(7000, 'localhost')
    sock.bind(sockaddr)
    sock.listen(0)

    client, client_sockaddr = sock.accept
    print "Connected\n"
    print ">>> #{client.readline}"
    abort



    Client
    --------
    require 'socket'
    include Socket::Constants

    $sock = Socket.new(AF_INET, SOCK_STREAM, 0)
    sockaddr = Socket.pack_sockaddr_in(7000, 'localhost')
    $sock.connect(sockaddr)

    def send_msg
    begin
    $sock.write("Hello\n")
    rescue StandardError => err
    p err
    end
    end

    puts "1"
    send_msg
    sleep 0.5
    puts "2"
    send_msg # Should error here.
    sleep 0.5
    puts "3"
    send_msg

    Any suggestions? I'm running on Linux, BTW.

    Thanks,
    Marc
     
    Marc Soda, Jan 22, 2007
    #1
    1. Advertising

  2. Marc Soda

    Guest

    On Jan 22, 2007, at 10:53 AM, Marc Soda wrote:
    > Any suggestions? I'm running on Linux, BTW.


    What you are seeing is in the nature of TCP.

    After the connection succeeds your server shuts down *its* end of
    the TCP session (i.e. it tells the client that it won't send any
    more data). Since TCP is full duplex, the client-> server part of
    the connection is still up and running as far as the client is
    concerned.

    Your first write stuffs the data in a buffer and returns (with no
    error). Shortly thereafter the kernel decides to actually transmit
    the data. The server responds with a RESET indicating that it will
    no longer accept data on that TCP session. The error is noted by
    the kernel and is reported to your client code on the *next* attempt
    to write to the socket (which is now in an error state).

    There is no one-to-one mapping of a write call on the socket to
    data transmission on the wire for TCP due to buffering. This is
    why the first write doesn't cause the transmit/detect/report of
    the error.

    Gary Wright
     
    , Jan 22, 2007
    #2
    1. Advertising

  3. Marc Soda

    Marc Soda Guest

    On 1/22/07, <> wrote:
    > What you are seeing is in the nature of TCP.
    >
    > After the connection succeeds your server shuts down *its* end of
    > the TCP session (i.e. it tells the client that it won't send any
    > more data). Since TCP is full duplex, the client-> server part of
    > the connection is still up and running as far as the client is
    > concerned.
    >
    > Your first write stuffs the data in a buffer and returns (with no
    > error). Shortly thereafter the kernel decides to actually transmit
    > the data. The server responds with a RESET indicating that it will
    > no longer accept data on that TCP session. The error is noted by
    > the kernel and is reported to your client code on the *next* attempt
    > to write to the socket (which is now in an error state).
    >
    > There is no one-to-one mapping of a write call on the socket to
    > data transmission on the wire for TCP due to buffering. This is
    > why the first write doesn't cause the transmit/detect/report of
    > the error.
    >
    > Gary Wright
    >
    >
    >
    >
    >


    Gary,

    First, thanks for your answer. I agree with you, however, my
    understanding was that after the client side kernel flushes it's
    buffers from the second send(2) (the first send after the server dies)
    the server would send a RST back. If I were to go about other
    processing and not even touch the socket again I should still receive
    and SIGPIPE. Is this not true?

    Thanks for your time.
    Marc
     
    Marc Soda, Jan 23, 2007
    #3
  4. Marc Soda

    Guest

    On Jan 22, 2007, at 10:59 PM, Marc Soda wrote:
    > First, thanks for your answer. I agree with you, however, my
    > understanding was that after the client side kernel flushes it's
    > buffers from the second send(2) (the first send after the server dies)
    > the server would send a RST back. If I were to go about other
    > processing and not even touch the socket again I should still receive
    > and SIGPIPE. Is this not true?


    The SIGPIPE is sent when you attempt to write to the socket, not when
    the kernel receives the RST. So in your example, the timeline looks
    like:

    client server
    ------------------------------
    listen
    connect
    accept
    abort
    <-----------FIN
    write
    sleep
    | 'hello' -------->
    | <----------- RST
    write: SIGPIPE


    That is a pretty lame diagram but I hope it clarifies things.

    FYI, this is a behavior of the underlying socket abstraction, not
    something particular to Ruby.


    Gary Wright
     
    , Jan 23, 2007
    #4
    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. Laszlo Nagy
    Replies:
    1
    Views:
    4,858
    Mark Wooding
    Jan 27, 2009
  2. Jean-Paul Calderone
    Replies:
    0
    Views:
    974
    Jean-Paul Calderone
    Jan 27, 2009
  3. Laszlo Nagy
    Replies:
    0
    Views:
    553
    Laszlo Nagy
    Feb 1, 2009
  4. Steve Holden
    Replies:
    0
    Views:
    669
    Steve Holden
    Feb 1, 2009
  5. Steve Holden
    Replies:
    1
    Views:
    722
Loading...

Share This Page