Question about non-blocking NIO and Selection Keys
I am new to non-blocking socket i/o so please go easy on my terrible
code below :)
That being said, I have a socket that must be used for reading and
writing, but nothing else. So I initialize my socket early on as:
asyncSelector = new Selector();
sockC.configureBlocking(false);
sockC.register(asyncSelector, SelectionKey.OP_WRITE |
SelectionKey.OP_READ);
Now, whenever I need to write to my socket, I do the following:
public void write(ByteBuffer buf) throws ServerException {
int bytesWritten = 0;
int bytesToWrite = buf.remaining();
while (bytesWritten < bytesToWrite) {
limitBuffer(buf, bytesToWrite, bytesWritten,
MAX_BYTES_PER_BUF_WRITE);
waitForOperation(SelectionKey.OP_WRITE);
int bytesWrittenThisTime = sockC.write(buf);
if (bytesWrittenThisTime == -1)
throw new ServerException("Remote host unexpectedly closed
the connection");
else
bytesWritten += bytesWrittenThisTime;
}
}
public void waitForOperation(int operation) throws ServerException {
while (true) {
try {
if (asyncSelector.select() == 0)
continue;
Set<SelectionKey> selected = asyncSelector.selectedKeys();
for (Iterator<SelectionKey> i = selected.iterator();
i.hasNext(); ) {
if ((i.next().readyOps() & operation) != 0)
return;
}
}
catch (IOException e) {
throw new ServerException("An error occured while performing
a socket operation.");
}
}
}
When I need to read from the socket, I do something very similar.
Now first let me say I know this is poorly designed, but all of this
code is contained in a class that was originally designed to
encapsulate a blocking socket. Then an entire application was built
around this class, and then a specific need was encountered to make it
non-blocking, and short of re-designing the entire application (which
is not an option at all) this seemed like the best way to go about
doing it.
The ultimate problem I was trying to solve is that I had a blocking
socket that would start sending tons of data, gigabytes even. At any
point in time, the client might say "I don't want this data anymore",
and instead of waiting for 17 gigabytes fo data to be sent, which
could take quite a while, it should be able to send the server a code
which would make the server stop sending data. Before we had this
need, a blocking socket was fine. But now, the server breaks the data
up into chunks and sends it in a tight loop. The first line of the
loop simply checks if SelectionKey.OP_READ is selected and if so, it
can read the code from the client and possibly stop sending data.
The problem I am having is that if I run a previous version of the
application it is much, much, much faster. I'm not sure how to tell
if this NIO is even the culprit, as a few other changes were made as
well, but this one is the most fundamental and highest visibility, and
as such I think it's most likely to be the culprit. Can anyone
provide any observations or suggestions as how to improve this code?
Thanks