Non-Blocking Socket and BufferedInputStream

Discussion in 'Java' started by mshetty@mail.com, Dec 29, 2006.

  1. Guest

    Hi,

    We are new to Java. We are using some exising code which does a
    byte-by-byte read from the socket. The data that is being read is an
    xml string. A byte-by-byte read takes about 150 to 200 milliseconds. We
    need to improve the response time.

    The application uses BufferedInputStream's read method to read the
    data. On the net we found some sites which suggested doing read in
    chunks (read an array of bytes).
    Looping until read returns -1 does not work as read block which the
    data read is complete.

    As per the documentation the read API should return -1 if there is no
    data.

    Similarly DataInputStream's readFully also blocks.

    Is there a non-blocking way of doing a read in Java?

    Would help if you suggest some alternative.

    Thanks and Regards,
    M Shetty
     
    , Dec 29, 2006
    #1
    1. Advertising

  2. Derek Tandy Guest

    Code Listing 18-4: NonBlockingServer.java


    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    import java.net.*;
    import java.util.*;

    public class NonBlockingServer
    {
    public static void main(String[] args)
    {
    try
    {
    Selector selector = Selector.open();

    ServerSocketChannel serverSocketChannel =
    ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);

    ServerSocket serverSocket = serverSocketChannel.socket();

    serverSocket.bind(new InetSocketAddress(9000));

    System.out.println("Non-blocking Server created on port
    9000");

    serverSocketChannel.register(selector,
    SelectionKey.OP_ACCEPT);

    System.out.println("Waiting for client connections...");

    int amountToProcess = 0;
    while(true)
    {
    amountToProcess = selector.selectNow();

    if(amountToProcess > 0)
    {
    try
    {
    Set keys = selector.selectedKeys();

    Iterator iterator = keys.iterator();

    while(iterator.hasNext())
    {
    SelectionKey selectionKey =
    (SelectionKey) iterator.next();
    iterator.remove(); // remove the key


    int operation = selectionKey
    .interestOps();

    if((SelectionKey.OP_ACCEPT & operation)
    != 0)
    {
    // Accept the connection...
    ServerSocketChannel channel =
    (ServerSocketChannel)
    selectionKey.channel();
    SocketChannel socket =
    channel.accept();
    socket.configureBlocking(false);

    // register for a writing operation
    socket.register(selector,
    SelectionKey.OP_WRITE);

    System.out.println("Client
    Connected...");
    }
    else if((SelectionKey.OP_READ &
    operation) != 0)
    {
    // Attempt to read...
    System.out.println("About to read
    from client...");

    SocketChannel socket =
    (SocketChannel) selectionKey
    .channel();

    // get the message from the client...
    ByteBuffer incomingLengthInBytes =
    ByteBuffer.allocate(4);
    // size of an 'int'
    socket.read(incomingLengthInBytes);
    incomingLengthInBytes.rewind();
    int incomingLength =
    incomingLengthInBytes.getInt();
    System.out.println("Got Incoming
    Length as: "+incomingLength+"
    bytes");

    // now allocate the correct size for
    // the message...
    ByteBuffer incomingData = ByteBuffer
    .allocate(incomingLength);
    socket.read(incomingData);
    incomingData.rewind();
    String string = incomingData
    .asCharBuffer().toString();


    // Finally print received message...
    System.out.println("Received:
    "+string);

    // terminate the connection...
    socket.close();
    }
    else if((SelectionKey.OP_WRITE &
    operation) != 0)
    {
    // Attempt to write...


    System.out.println("Now going to
    write to client...");

    SocketChannel socket =
    (SocketChannel) selectionKey
    .channel();

    socket.register(selector,
    SelectionKey.OP_READ);

    String stringToSend = "This is a
    message";

    int length = stringToSend.length()
    * 2;

    ByteBuffer lengthInBytes =
    ByteBuffer.allocate(4);
    // 4 = size of a 'int'
    ByteBuffer dataToSend =
    ByteBuffer.allocate(length);

    lengthInBytes.putInt(length);
    lengthInBytes.rewind();
    dataToSend.asCharBuffer()
    .put(stringToSend);

    ByteBuffer sendArray[] =
    {lengthInBytes, dataToSend};

    socket.write(sendArray);
    //socket.close();
    System.out.println("Sent Message to
    Client...");
    }
    }
    }
    catch(IOException e)
    {
    System.out.println(e);
    }
    }
    }
    }
    catch(IOException e)
    {
    System.out.println(e);
    }

    }
    }




    Code Listing 18-5: NonBlockingClient.java


    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    import java.net.*;

    public class NonBlockingClient
    {
    public static void main(String[] args)
    {
    try
    {
    SocketChannel socketChannel = SocketChannel.open();
    socketChannel.connect(new InetSocketAddress("127.0.0.1",
    9000));

    // wait for the message from the server...
    ByteBuffer incomingLengthInBytes =
    ByteBuffer.allocate(4); // size of an 'int'
    socketChannel.read(incomingLengthInBytes);
    incomingLengthInBytes.rewind();
    int incomingLength = incomingLengthInBytes.getInt();
    System.out.println("Got Incoming Length as:
    "+incomingLength+" bytes");

    // now allocate the correct size for the message...
    ByteBuffer incomingData =
    ByteBuffer.allocate(incomingLength);
    socketChannel.read(incomingData);
    incomingData.rewind();
    String string = incomingData.asCharBuffer().toString();


    // Finally print the received message...
    System.out.println("Received: "+string);


    // Send a message back to the server...
    String replyMessage = "Message Received - Thank you!";
    int length = replyMessage.length() * 2;

    ByteBuffer replyLength = ByteBuffer.allocate(4);
    replyLength.putInt(length);
    replyLength.rewind();

    ByteBuffer replyText = ByteBuffer.allocate(length);
    replyText.asCharBuffer().put(replyMessage);

    ByteBuffer toSend[] = {replyLength, replyText};
    socketChannel.write(toSend);

    }
    catch(IOException e)
    {
    System.out.println(e);
    }

    }
    }


    On Dec 29, 5:23 am, wrote:
    > Hi,
    >
    > We are new to Java. We are using some exising code which does a
    > byte-by-byte read from the socket. The data that is being read is an
    > xml string. A byte-by-byte read takes about 150 to 200 milliseconds. We
    > need to improve the response time.
    >
    > The application uses BufferedInputStream's read method to read the
    > data. On the net we found some sites which suggested doing read in
    > chunks (read an array of bytes).
    > Looping until read returns -1 does not work as read block which the
    > data read is complete.
    >
    > As per the documentation the read API should return -1 if there is no
    > data.
    >
    > Similarly DataInputStream's readFully also blocks.
    >
    > Is there a non-blocking way of doing a read in Java?
    >
    > Would help if you suggest some alternative.
    >
    > Thanks and Regards,
    > M Shetty
     
    Derek Tandy, Dec 29, 2006
    #2
    1. Advertising

  3. wrote:
    > Hi,
    >
    > We are new to Java. We are using some exising code which does a
    > byte-by-byte read from the socket. The data that is being read is an
    > xml string. A byte-by-byte read takes about 150 to 200 milliseconds. We
    > need to improve the response time.


    How much data are you reading in 200 ms?

    > The application uses BufferedInputStream's read method to read the
    > data. On the net we found some sites which suggested doing read in
    > chunks (read an array of bytes).
    > Looping until read returns -1 does not work as read block which the
    > data read is complete.


    The -1 is for when the end of stream has been reached, for example you
    attempt to read one more byte than exists in a file. You said you were
    reading from a socket? Is the server closing when you've sent all the
    data you want to send? If it is you will throw an IOException in the
    read and -1 never comes into play.

    > As per the documentation the read API should return -1 if there is no
    > data.


    Probably not.

    > Similarly DataInputStream's readFully also blocks.
    >
    > Is there a non-blocking way of doing a read in Java?
    >
    > Would help if you suggest some alternative.


    Can you do any more work if you don't have all the data? If not I would
    just play with the buffer size of the BufferedInputStream. It is
    capable of very fast I/O. What is the size of the buffer now?

    --

    Knute Johnson
    email s/nospam/knute/
     
    Knute Johnson, Dec 29, 2006
    #3
  4. EJP Guest

    Derek Tandy wrote:

    > SocketChannel socket =
    > channel.accept();
    > socket.configureBlocking(false);


    OK so far ...

    > // register for a writing operation
    > socket.register(selector,
    > SelectionKey.OP_WRITE);


    Why? OP_WRITE is almost always true. This will cause a hard loop in the
    selection loop. Usually a server will register for OP_READ at this point.

    > // get the message from the client...
    > ByteBuffer incomingLengthInBytes =
    > ByteBuffer.allocate(4);


    This is wasteful. You're always going to need this buffer, allocate it once.

    > // size of an 'int'
    > socket.read(incomingLengthInBytes);


    At this point you are blindly assuming that you've read all 4 bytes. And
    you've ignored the possible cases of reading 0 bytes or EOF.

    > // now allocate the correct size for
    > // the message...


    Again this is wasteful. Why not just allocate a ByteBuffer large enough
    for any message? and then you can do both reads at the same time, into
    the same buffer even.

    > ByteBuffer incomingData = ByteBuffer
    > .allocate(incomingLength);
    > socket.read(incomingData);


    At this point you've again blindly assumed that you've read the entire
    message. And again you've ignored the possible cases of reading 0 bytes
    or EOF.

    None of this is good enough. You have to read until the message is
    complete in both cases. You have to cope with a read count of zero. You
    have to cope with EOF, premature or otherwise.

    > incomingData.rewind();
    > String string = incomingData
    > .asCharBuffer().toString();
    >
    >
    > // Finally print received message...
    > System.out.println("Received:
    > "+string);


    Received something you mean. There's no guarantee that it's even
    complete enough to decode successfully to a String.

    > // terminate the connection...
    > socket.close();


    ....

    > socket.write(sendArray);


    And again you've ignored the return code so you can't detect a short
    write or a full send buffer.

    > socketChannel.read(incomingLengthInBytes);


    Again you've ignored the return value, see above.

    > socketChannel.read(incomingData);


    And again ...

    > socketChannel.write(toSend);


    And again ...

    I'm sorry but this code is not good enough for serious use.
     
    EJP, Dec 30, 2006
    #4
  5. Daniel Pitts Guest

    wrote:
    > Hi,
    >
    > We are new to Java. We are using some exising code which does a
    > byte-by-byte read from the socket. The data that is being read is an
    > xml string. A byte-by-byte read takes about 150 to 200 milliseconds. We
    > need to improve the response time.
    >
    > The application uses BufferedInputStream's read method to read the
    > data. On the net we found some sites which suggested doing read in
    > chunks (read an array of bytes).
    > Looping until read returns -1 does not work as read block which the
    > data read is complete.
    >
    > As per the documentation the read API should return -1 if there is no
    > data.
    >
    > Similarly DataInputStream's readFully also blocks.
    >
    > Is there a non-blocking way of doing a read in Java?
    >
    > Would help if you suggest some alternative.
    >
    > Thanks and Regards,
    > M Shetty


    There are two approaches... Using NIO, or using Threads.

    I would suggest starting with the Threads approach, it is a lot more
    straight forward.

    Good luck.
     
    Daniel Pitts, Dec 30, 2006
    #5
  6. Hi,

    "EJP" <> wrote in message
    news:%kjlh.15543$...
    > Again this is wasteful. Why not just allocate a ByteBuffer large enough
    > for any message? and then you can do both reads at the same time, into
    > the same buffer even.


    Maybe not in this simple case, but one reason for not combining the reads
    into a "maxi" buffer would be that you'd possibly retrieve multiple messages
    rather than just the first one you're interested in. Although, I certainly
    agree with allocating the buffer at the start.

    Anyway, sorry for the interjection but here are some newbie questions if
    anyone has the time or inclination: -

    1) Is it correct that NIO and Channels are the preferred, and more
    efficient, options for Java Socket communication these days? (As opposed to
    IO and Net.Socket classes) Other Buffers and read() methods seem to imply
    loops round single-byte reads, and I've never really understood mark(), so
    I'm quite taken by the look of this example.

    2) If I go the "Preallocate a maxi-buff at initialize time" route, (and I
    personally have a 2-byte message header denoting an implicit length) would
    it sound reasonable to do a .read(headerbuff) and if it's a type "A1" record
    then databuff.limit(a1length) before the subsequent .read(databuff)

    3) Why in the Derek's example does the length have to be multiplied by 2
    each time? Surely bytes is bytes is bytes?

    4) It looks like an operation on a buffer/bytebuffer is dependent on the
    current values of position and limit (and capacity obviously). Is this true,
    and is this why you must always rewind() the buffer after the socket read
    before doing any work on it?

    5) When does a buffer get marked for removal or garbage collected?

    6) I really like the simplicity of the following: -
    > ByteBuffer toSend[] = {replyLength, replyText};
    > socketChannel.write(toSend);

    Is there an equvilently easy Unstring opertaion for plucking fields out of a
    read network message, or is it better/de rigueur to to use the
    GETprimative() methods to obtain each field from the buffer in turn? Given
    that things like Endianness and Character-set seem to be properties of
    Buffers then maybe the second strategy makes more sense.

    Any help (or other examples) greatly appreciated.

    Regards Richard Maher

    PS.
    > I'm sorry but this code is not good enough for serious use.

    Esmond, I believe you've written books on this and I'm sure they're readily
    available, but it you'd like to provide an example here of code that *is*
    good enough for serious use here then I, for one, would certainly like to
    see it! And as someone who grew up in salubrious E.Coburg and now resides in
    Perth, "Your weather's rubbish!" :)

    "EJP" <> wrote in message
    news:%kjlh.15543$...
    > Derek Tandy wrote:
    >
    > > SocketChannel socket =
    > > channel.accept();
    > > socket.configureBlocking(false);

    >
    > OK so far ...
    >
    > > // register for a writing operation
    > > socket.register(selector,
    > > SelectionKey.OP_WRITE);

    >
    > Why? OP_WRITE is almost always true. This will cause a hard loop in the
    > selection loop. Usually a server will register for OP_READ at this point.
    >
    > > // get the message from the client...
    > > ByteBuffer incomingLengthInBytes =
    > > ByteBuffer.allocate(4);

    >
    > This is wasteful. You're always going to need this buffer, allocate it

    once.
    >
    > > // size of an 'int'
    > > socket.read(incomingLengthInBytes);

    >
    > At this point you are blindly assuming that you've read all 4 bytes. And
    > you've ignored the possible cases of reading 0 bytes or EOF.
    >
    > > // now allocate the correct size for
    > > // the message...

    >
    > Again this is wasteful. Why not just allocate a ByteBuffer large enough
    > for any message? and then you can do both reads at the same time, into
    > the same buffer even.
    >
    > > ByteBuffer incomingData = ByteBuffer
    > > .allocate(incomingLength);
    > > socket.read(incomingData);

    >
    > At this point you've again blindly assumed that you've read the entire
    > message. And again you've ignored the possible cases of reading 0 bytes
    > or EOF.
    >
    > None of this is good enough. You have to read until the message is
    > complete in both cases. You have to cope with a read count of zero. You
    > have to cope with EOF, premature or otherwise.
    >
    > > incomingData.rewind();
    > > String string = incomingData
    > > .asCharBuffer().toString();
    > >
    > >
    > > // Finally print received message...
    > > System.out.println("Received:
    > > "+string);

    >
    > Received something you mean. There's no guarantee that it's even
    > complete enough to decode successfully to a String.
    >
    > > // terminate the connection...
    > > socket.close();

    >
    > ...
    >
    > > socket.write(sendArray);

    >
    > And again you've ignored the return code so you can't detect a short
    > write or a full send buffer.
    >
    > > socketChannel.read(incomingLengthInBytes);

    >
    > Again you've ignored the return value, see above.
    >
    > > socketChannel.read(incomingData);

    >
    > And again ...
    >
    > > socketChannel.write(toSend);

    >
    > And again ...
    >
    > I'm sorry but this code is not good enough for serious use.
     
    Richard Maher, Dec 31, 2006
    #6
  7. EJP Guest

    Richard Maher wrote:

    > Maybe not in this simple case, but one reason for not combining the reads
    > into a "maxi" buffer would be that you'd possibly retrieve multiple messages
    > rather than just the first one you're interested in.


    There's nothing wrong with that, as a strategy.

    > 1) Is it correct that NIO and Channels are the preferred, and more
    > efficient, options for Java Socket communication these days?


    Not really. Only if you need about 20% extra performance and/or the
    extra facilities such as multiplexing or NBIO, or the ability to
    transfer between channels. Using NIO is a major design decision as it
    affects everything, don't enter on it lightly. For example I would
    rarely if ever use it at a client.

    > 2) If I go the "Preallocate a maxi-buff at initialize time" route, (and I
    > personally have a 2-byte message header denoting an implicit length) would
    > it sound reasonable to do a .read(headerbuff) and if it's a type "A1" record
    > then databuff.limit(a1length) before the subsequent .read(databuff)


    Yes.

    > 3) Why in the Derek's example does the length have to be multiplied by 2
    > each time? Surely bytes is bytes is bytes?


    Because he's converting Java chars to bytes, although again this is not
    the correct way to do it: there are java.nio.charset methods for that.

    > 4) It looks like an operation on a buffer/bytebuffer is dependent on the
    > current values of position and limit (and capacity obviously). Is this true,
    > and is this why you must always rewind() the buffer after the socket read
    > before doing any work on it?


    Yes, or clear it, or best of all consume the data with compact() - this
    lets you cope with any read-aheads that may have happened.

    > 5) When does a buffer get marked for removal or garbage collected?


    Same as any other Java object, except for FileChannel mapped buffers
    which are never collected (grrr).

    > 6) I really like the simplicity of the following: -
    >
    >> ByteBuffer toSend[] = {replyLength, replyText};
    >> socketChannel.write(toSend);

    >
    > Is there an equvilently easy Unstring opertaion for plucking fields out of a
    > read network message, or is it better/de rigueur to to use the
    > GETprimative() methods to obtain each field from the buffer in turn? Given
    > that things like Endianness and Character-set seem to be properties of
    > Buffers then maybe the second strategy makes more sense.


    There are scattering-read methods corresponding to these gathering-write
    methods. Very useful when you have a fixed-length header, message, and
    trailer. In the case of a length word you generally need the length
    value before you read the content, so you don't use them for that.
     
    EJP, Dec 31, 2006
    #7
    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. Hendra Gunawan
    Replies:
    1
    Views:
    13,066
    Allan Herriman
    Apr 8, 2004
  2. Andre Kelmanson

    blocking i/o vs. non blocking i/o (performance)

    Andre Kelmanson, Oct 10, 2003, in forum: C Programming
    Replies:
    3
    Views:
    996
    Valentin Tihomirov
    Oct 12, 2003
  3. Tim Black
    Replies:
    1
    Views:
    1,374
    Alan Kennedy
    Aug 3, 2004
  4. nukleus
    Replies:
    14
    Views:
    910
    Chris Uppal
    Jan 22, 2007
  5. Serge Savoie
    Replies:
    4
    Views:
    292
    Serge Savoie
    Oct 1, 2008
Loading...

Share This Page