Non-Blocking Socket and BufferedInputStream

M

mshetty

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
 
D

Derek Tandy

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);
}

}
}
 
K

Knute Johnson

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?
 
E

EJP

Derek said:
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.
 
D

Daniel Pitts

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.
 
R

Richard Maher

Hi,

EJP said:
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!" :)
 
E

EJP

Richard said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top