strange sockets

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Skink

    #1

    strange sockets

    Hi,

    I'm preparing a python server that sends java classes and resources to
    custom java class loader. In order to make it faster I don't want to use
    URLClassLoader that uses HTTP protocol 1.0 and for each class/resource
    creates own connection.
    Instead I'd like to use raw sockets with simple protocol:

    - class loader sends a line terminated with \n with resource to get
    - python server reads that line, gets the file and sends back an
    integer with file length and then the file itself
    - class loader reads a lenght integer and then reads the remainig data


    The problem is when I try to read several files the first one is read
    quite fast, but the rest is read 40 x slower. For example (time is in
    seconds):

    % python client.py client.py client.py client.py server.py server.py
    init 0.0006608963012 7
    client.py 0.0009548664093 02
    client.py 0.0408389568329
    client.py 0.0409188270569
    server.py 0.0409059524536
    server.py 0.0409259796143

    what's wrong here?

    thanks,
    skink

    client.py
    ------------------------------------------------------------------------------------
    import socket, sys, struct, time

    HOST = 'localhost'
    PORT = 8080
    t1 = time.time()
    s = socket.socket(s ocket.AF_INET, socket.SOCK_STR EAM)
    s.connect((HOST , PORT))
    t2 = time.time()
    print "init", t2-t1
    for arg in sys.argv[1:]:
    t1 = time.time()
    s.send(arg + "\n")
    len, = struct.unpack(" !i", s.recv(4))
    data = s.recv(len)
    t2 = time.time()
    print arg, t2-t1
    s.close()
    ------------------------------------------------------------------------------------

    server.py
    ------------------------------------------------------------------------------------
    import socket, struct, binascii

    HOST = ''
    PORT = 8080
    s = socket.socket(s ocket.AF_INET, socket.SOCK_STR EAM)
    s.bind((HOST, PORT))
    while 1:
    s.listen(1)
    conn, addr = s.accept()
    print 'Connected by', addr
    f = conn.makefile()
    while 1:
    resource = f.readline().rs trip()
    print "[%s]" % resource
    if not resource:
    break
    data = open(resource, "rb").read( )
    conn.sendall(st ruct.pack("!i", len(data)))
    conn.sendall(da ta)
    conn.close()
    ------------------------------------------------------------------------------------
  • Sion Arrowsmith

    #2
    Re: strange sockets

    In article <dkfqhb$l1l$1@o pal.futuro.pl>, Skink <spam@me.please > wrote:[color=blue]
    >% python client.py client.py client.py client.py server.py server.py
    >init 0.0006608963012 7
    >client.py 0.0009548664093 02
    >client.py 0.0408389568329
    >client.py 0.0409188270569
    >server.py 0.0409059524536
    >server.py 0.0409259796143
    >
    >what's wrong here?[/color]

    That smells of a Nagle/delayed ACK problem to me (see, for instance,
    http://www.port80software.com/200ok/...1/31/317.aspx). 40ms
    is the default delayed ACK timeout on Linux, IIRC (pretty much
    everything else uses 200ms). I *think* what's happening from the
    server's point of view is:

    receive request 1
    send length (first undersized packet is sent immediately by Nagle)
    (client delays ack #1)
    send data (larger than 1 packet, send immediately)
    (client delays acks #2--#n)
    receive request 2 with ack #1
    buffer sending length (undersized packet, not received last ack)
    -> 40ms passes <-
    (client timesout delayed acks and sends)
    send length
    send data (as before)

    although why the undersized packet at the end of the first chunk of
    data isn't buffered, I don't know.

    Solutions: either change
    [color=blue]
    > conn.sendall(st ruct.pack("!i", len(data)))
    > conn.sendall(da ta)[/color]

    to

    conn.sendall(st ruct.pack("!i", len(data)) + data)

    or after creating conn

    conn.setsockopt (socket.SOL_TCP , socket.TCP_NODE LAY, 1)

    to disable Nagle.

    --
    \S -- siona@chiark.gr eenend.org.uk -- http://www.chaos.org.uk/~sion/
    ___ | "Frankly I have no feelings towards penguins one way or the other"
    \X/ | -- Arthur C. Clarke
    her nu becomeþ se bera eadward ofdun hlæddre heafdes bæce bump bump bump

    Comment

    • Jim Segrave

      #3
      Re: strange sockets

      In article <dkfqhb$l1l$1@o pal.futuro.pl>, Skink <spam@me.please > wrote:[color=blue]
      >Hi,
      >
      >I'm preparing a python server that sends java classes and resources to
      >custom java class loader. In order to make it faster I don't want to use
      >URLClassLoad er that uses HTTP protocol 1.0 and for each class/resource
      >creates own connection.
      >Instead I'd like to use raw sockets with simple protocol:
      >
      > - class loader sends a line terminated with \n with resource to get
      > - python server reads that line, gets the file and sends back an
      >integer with file length and then the file itself
      > - class loader reads a lenght integer and then reads the remainig data
      >
      >
      >The problem is when I try to read several files the first one is read
      >quite fast, but the rest is read 40 x slower. For example (time is in
      >seconds):
      >
      >% python client.py client.py client.py client.py server.py server.py
      >init 0.0006608963012 7
      >client.py 0.0009548664093 02
      >client.py 0.0408389568329
      >client.py 0.0409188270569
      >server.py 0.0409059524536
      >server.py 0.0409259796143
      >
      >what's wrong here?[/color]

      At a guess, what you've measured is how long it takes to transfer the
      data to the underlying OS socket buffers. The first transfer fills the
      buffers, subsequent ones have to wait until the data has been put on
      the wire and acknowledged before there's space for the writes.

      --
      Jim Segrave (jes@jes-2.demon.nl)

      Comment

      • Bryan Olson

        #4
        Re: strange sockets

        Skink wrote:
        [...]
        [color=blue]
        > what's wrong here?[/color]

        Sion Arrowsmith is right about what causes the delay.
        Just in case your real code looks like this, I'll note:
        [color=blue]
        > len, = struct.unpack(" !i", s.recv(4))
        > data = s.recv(len)[/color]

        First, you almost certainly don't want to use the name 'len'.
        Ought not to be allowed. Second, recv can return fewer bytes
        than requested, even when the connection is still open for
        reading. You might replace the lines above with (untested):

        length = struct.unpack(" !i", s.recv(4))
        data = []
        while length:
        data.append(s.r ecv(length))
        length -= len(data[-1])
        data = ''.join(data)


        There's still a robustness problem, but in the absense of errors
        and malice, that should work. I think.


        --
        --Bryan

        Comment

        • Skink

          #5
          Re: strange sockets

          Sion,
          [color=blue]
          > Solutions: either change
          >
          >[color=green]
          >> conn.sendall(st ruct.pack("!i", len(data)))
          >> conn.sendall(da ta)[/color]
          >
          >
          > to
          >
          > conn.sendall(st ruct.pack("!i", len(data)) + data)
          >
          > or after creating conn
          >
          > conn.setsockopt (socket.SOL_TCP , socket.TCP_NODE LAY, 1)
          >
          > to disable Nagle.[/color]

          thank yuo so much, both solutions work perfect!

          %python client.py client.py client.py client.py
          init 0.0010118484497 1
          client.py 0.0005869865417 48
          client.py 0.0004489421844 48
          client.py 0.0004701614379 88

          I think that I'll use the second one


          skink

          Comment

          • Skink

            #6
            Re: strange sockets

            Bryan,[color=blue]
            >
            > Sion Arrowsmith is right about what causes the delay.
            > Just in case your real code looks like this, I'll note:
            >[color=green]
            >> len, = struct.unpack(" !i", s.recv(4))
            >> data = s.recv(len)[/color][/color]
            yes, my mistake ;)
            [color=blue]
            >
            >
            > First, you almost certainly don't want to use the name 'len'.
            > Ought not to be allowed. Second, recv can return fewer bytes
            > than requested, even when the connection is still open for
            > reading. You might replace the lines above with (untested):
            >
            > length = struct.unpack(" !i", s.recv(4))
            > data = []
            > while length:
            > data.append(s.r ecv(length))
            > length -= len(data[-1])
            > data = ''.join(data)
            >
            >[/color]
            i know, i know, i sent fake python client: the real will be done in java.
            [color=blue]
            > There's still a robustness problem, but in the absense of errors
            > and malice, that should work. I think.
            >
            >[/color]

            Comment

            • Skink

              #7
              Re: strange sockets

              Sion Arrowsmith wrote:[color=blue]
              >
              > conn.sendall(st ruct.pack("!i", len(data)) + data)
              >
              > or after creating conn
              >
              > conn.setsockopt (socket.SOL_TCP , socket.TCP_NODE LAY, 1)
              >
              > to disable Nagle.
              >[/color]

              Sion,

              thank you for your help,

              it works but...
              it works when client & server is in python
              i tried both solutions and they work when client is client.py
              they both don't work when client is java client
              when i tried to connect python's server by java client i have the same:

              % java Loader server.py server.py server.py
              init 29
              server.py reading 631 1
              server.py reading 631 40
              server.py reading 631 41

              why?

              thanks,
              skink.

              Comment

              • Steve Holden

                #8
                Re: strange sockets

                Skink wrote:[color=blue]
                > Sion Arrowsmith wrote:
                >[color=green]
                >>conn.sendall( struct.pack("!i ", len(data)) + data)
                >>
                >>or after creating conn
                >>
                >>conn.setsocko pt(socket.SOL_T CP, socket.TCP_NODE LAY, 1)
                >>
                >>to disable Nagle.
                >>[/color]
                >
                >
                > Sion,
                >
                > thank you for your help,
                >
                > it works but...
                > it works when client & server is in python
                > i tried both solutions and they work when client is client.py
                > they both don't work when client is java client
                > when i tried to connect python's server by java client i have the same:
                >
                > % java Loader server.py server.py server.py
                > init 29
                > server.py reading 631 1
                > server.py reading 631 40
                > server.py reading 631 41
                >
                > why?
                >[/color]
                Seems to me that should probably be a question for comp.lang.java.

                regards
                Steve
                --
                Steve Holden +44 150 684 7255 +1 800 494 3119
                Holden Web LLC www.holdenweb.com
                PyCon TX 2006 www.python.org/pycon/

                Comment

                • Skink

                  #9
                  Re: strange sockets

                  Steve Holden wrote:[color=blue]
                  > Skink wrote:
                  >[color=green]
                  >> Sion Arrowsmith wrote:
                  >>[color=darkred]
                  >>> conn.sendall(st ruct.pack("!i", len(data)) + data)
                  >>>
                  >>> or after creating conn
                  >>>
                  >>> conn.setsockopt (socket.SOL_TCP , socket.TCP_NODE LAY, 1)
                  >>>
                  >>> to disable Nagle.
                  >>>[/color]
                  >>
                  >>
                  >> Sion,
                  >>
                  >> thank you for your help,
                  >>
                  >> it works but...
                  >> it works when client & server is in python
                  >> i tried both solutions and they work when client is client.py
                  >> they both don't work when client is java client
                  >> when i tried to connect python's server by java client i have the same:
                  >>
                  >> % java Loader server.py server.py server.py
                  >> init 29
                  >> server.py reading 631 1
                  >> server.py reading 631 40
                  >> server.py reading 631 41
                  >>
                  >> why?
                  >>[/color]
                  > Seems to me that should probably be a question for comp.lang.java.[/color]

                  ok, my falt. again... ;)
                  i forgot to use Buffered[Input|Output]Stream
                  [color=blue]
                  >
                  > regards
                  > Steve[/color]

                  Comment

                  Working...