I just wrote some code to try and implement a simple packet protocol using NIO.SocketChannel. I’m having trouble with reading data from the channels though. What I’m doing is reading in a certain number of bytes which form the header of my protocol during one selectNow() operation. I do this so I can create a new ByteBuffer with the exact size of the packet. That part works great. But then I never get another SelectionKey.isReadable() event from any future selectNow() operations. It’s like NIO just discards the rest of the data. Does anyone know why this would happen, or if there is a way to get the Selector to fire off another SelectionKey.isReadable() event?
Here’s my code (trimmed down to the vital stuff):
Snippet from SMLIOManager (the class that contains the Selector and lets the SMLIOSocketPacket classes know when their SelectionKeys are ready):
/*
* This is where the selector is checked for readiness
*/
void fire()
{
//The timer has fired, so check for I/O readiness
selector.selectedKeys().clear(); //Make sure the set is cleared
try
{
selector.selectNow(); //Fill the set with ready keys
}
catch (Exception e) //Some not so nice happened, so log it
{
LogManager.getLogManager().getLogger("sml.io").info("Exception in SMLIOManager.fire: " + e);
}
Iterator i = selector.selectedKeys().iterator(); //Get an iterator
if (selector.selectedKeys().size() > 0) //Log how many keys are ready (if there are any ready)
LogManager.getLogManager().getLogger("sml.io").finer("SMLIOManager.fire.selector.selectNow returned " +
selector.selectedKeys().size() + " of " +
selector.keys().size() + " keys.");
while (i.hasNext()) //Go through all the keys in the set
{
SelectionKey key = (SelectionKey)i.next(); //Get the next SelectionKey
((SMLIO)key.attachment()).stateReady(); //Tell the protocol manager that there is some I/O readiness, and to process it
}
}
Snippet from SMLIOSocketPacket (the class that implements the protocol) (and extends SMLIO)
void stateReady()
{
LogManager.getLogManager().getLogger("sml.io").info(name + " entered stateReady"); //Log entrance into the method
SocketChannel chan = (SocketChannel)key.channel(); //Get the channel
if (key.isWritable()) //Is writable, so write as much as possible
{
LogManager.getLogManager().getLogger("sml.io").info(name + ".key.isWritable"); //Log is writable
try
{
ByteBuffer out = (ByteBuffer)outQueue.getFirst(); //Get the first buffer in the queue of outgoing buffers
if (out.position() == out.limit()) //Reached the end of the buffer
{
outQueue.removeFirst(); //Remove the buffer
if (outQueue.size() > 0) //More buffers left in the queue, so use the next one
{
out = (ByteBuffer)outQueue.getFirst();
}
else //No more left
{
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); //Say we don't want to write anymore
out = null; //Prevent errors
}
}
if (out != null) //If the buffer exists
{
chan.write(out); //Write the buffer
}
}
catch (Exception e) //Something happened :(
{
LogManager.getLogManager().getLogger("sml.io").info(name + ".stateReady: " + e);
}
}
if (key.isReadable()) //Is readable, so read in as much as possible
{
LogManager.getLogManager().getLogger("sml.io").info(name + ".key.isReadable"); //Log readability
try
{
if (inBuff.position() == inBuff.limit()) //The buffer has been filled, so act appropriately
{
if (isHeader == true) //A header, so process it
{
inBuff.flip(); //Flip it to read the header
int len = inBuff.getInt(); //Get the length of the packet
inBuff = ByteBuffer.allocate(len); //Make a buffer to hold the packet
isHeader = false; //Not a header any more
}
else //The data itself
{
listener.recieveInput(inBuff, this); //Dispatch this packet (this is implemented in an outside class, but is yet to be called in pratice :( )
inBuff = ByteBuffer.allocate(HEADER_LEN); //Make a buffer to hold the header
isHeader = true; //Now a header
}
}
int val = chan.read(inBuff); //Read as much as we can (non-blocking)
LogManager.getLogManager().getLogger("sml.io").info(name + " read " + val + " bytes of data."); //Log how much has been written
}
catch (Exception e) //Something happened :(
{
LogManager.getLogManager().getLogger("sml.io").info("Exception in " + name + ".stateReady: " + e);
}
}
}
Looking at my log file, I noticed that only 1 “name read XXX bytes of data.” is recorded, and it was the size of the header. Also, there was only 1 “name.key.isReadable” in the log file as well.
Any suggestions???