Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Non-Blocking Socket and BufferedInputStream

Reply
Thread Tools

Non-Blocking Socket and BufferedInputStream

 
 
mshetty@mail.com
Guest
Posts: n/a
 
      12-29-2006
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

 
Reply With Quote
 
 
 
 
Derek Tandy
Guest
Posts: n/a
 
      12-29-2006
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, (E-Mail Removed) 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


 
Reply With Quote
 
 
 
 
Knute Johnson
Guest
Posts: n/a
 
      12-29-2006
http://www.velocityreviews.com/forums/(E-Mail Removed) 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/
 
Reply With Quote
 
EJP
Guest
Posts: n/a
 
      12-30-2006
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.
 
Reply With Quote
 
Daniel Pitts
Guest
Posts: n/a
 
      12-30-2006

(E-Mail Removed) 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.

 
Reply With Quote
 
Richard Maher
Guest
Posts: n/a
 
      12-31-2006
Hi,

"EJP" <(E-Mail Removed)> wrote in message
news:%kjlh.15543$(E-Mail Removed)...
> 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" <(E-Mail Removed)> wrote in message
news:%kjlh.15543$(E-Mail Removed)...
> 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.



 
Reply With Quote
 
EJP
Guest
Posts: n/a
 
      12-31-2006
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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
BufferedInputstream problem Daniel Java 7 03-10-2005 12:32 PM
Why do BufferedInputStream improve performance? Marc Twain Java 5 01-15-2004 07:17 PM
bad I/O performance in BufferedInputStream Jimmy Zhang Java 1 10-31-2003 04:40 AM
reset issue in bufferedInputStream Jimmy Zhang Java 6 10-15-2003 08:00 PM
FileInputStream alone VS BufferedInputStream wrapped FileInputStream Krick Java 2 08-28-2003 01:25 AM



Advertisments