Java – SocketChannel triggers isReadable() but no content is readable

SocketChannel triggers isReadable() but no content is readable… here is a solution to the problem.

SocketChannel triggers isReadable() but no content is readable

I’m having a new issue with my Android app. SocketChannel tells me it’s isReadable() but there’s nothing to read.

while(running)
    {
        int readyChannels = 0;
        try {
            readyChannels = selector.select();
        } catch (IOException e) {
            e.printStackTrace();
        }

if(readyChannels == 0) continue;

Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

while(keyIterator.hasNext())
        {
            SelectionKey key1 = keyIterator.next();

if(key1.isReadable())
            {
                publishProgress(1);
            }

else if(key1.isWritable())
            {
                publishProgress(2);
            }

else if(!key1.isValid())
            {
                Log.e("State", "progress error");
                publishProgress(3);
            }
        }
    }

I call the required function in onProgressUpdate. It’s a WebSocket app, so I need to send and receive handshakes. The send and receive handshake works with this while loop. First send SelectionKey.isWriteable() and the handshake, then send SelectionKey.isReadable() and read the handshake. But SelectionKey.isReadable() is still correct. The normal read function (not readHandshake) function is now called. But there is nothing to read. Here is the code for my read function :

public byte[] read(int nBytes)
{
    Log.e("State", "public byte[] read(int nBytes) start");

byte[] resultData = new byte[1024];
    ByteBuffer data = ByteBuffer.wrap(resultData);
    int remainingBytes = nBytes;

while(remainingBytes >= 0)
    {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = 0;
        try {
            bytesRead = channel.read(buffer);
        } catch (IOException e) {
            Log.e("ERROR", "channel read");
        }

if(bytesRead < 0)
        {
            sockState = WebSocketState.WebSocketErrorOccurred;
        }

else
        {
            remainingBytes = remainingBytes - bytesRead;
            data.put(buffer.array(), 0, bytesRead);
        }
    }

Log.e("State", "public byte[] read(int nBytes) done");

return resultData;
}

Now it’s stuck in an infinite loop. remainingBytes never < 0 because there is nothing readable and bytesRead remains at 0.

My question is, why is isReadable true when there is nothing to read?

Solution

If bytesRead < 0, you should close the channel or at least log out OP_READ. Otherwise, you will constantly receive OP_READ to tell you about EOS.

You should not allocate ByteBuffer once per read. Allocating ByteBuffers is an expensive operation. Assign each entry for the read method at least once. Better yet, assign one to each channel when you accept. You can keep it as or via a key attachment so you don’t lose it, so it disappears when you cancel the key or close the channel.

Related Problems and Solutions