logoff
logoff

Reputation: 3446

can I get available bytes to read from a Socket using Java NIO?

I want to know if there are available bytes to be read in a Java NIO Socket. In C/C++ it can be done using something like this:

int available(int fd){
  long readable = 0;

    if (ioctl(fd, FIONREAD, &readable) < 0){
      // error
    }

  return (int) readable;
}

It is possible to do the same operation with Java NIO (using SocketChannel, Selector, Socket, etc.)?

Upvotes: 5

Views: 5512

Answers (5)

logoff
logoff

Reputation: 3446

I have read tons of documentation, forums and, obviously each of your responses. I have developed and tested a solution. It is not the best, but it is my best approximation.

I use selectNow() method of Selector. Given a Selector named selector that have a SocketChannel registered, I can use this method to know if the associated Socket has readable bytes:

public boolean isReadable() {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while(keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if(!selectionKey.isValid()) {
                continue;
            }
            if(selectionKey.isReadable()) {
                return true;
            }
            keyIterator.remove();
        }
    } catch(IOException e) {
        // error
    }
    return false;
}

It is the only way I have found to know if a Socket has readable bytes without reading the bytes. Do you know a better way to do it or disadvantages of doing it by this way?

EDIT:

This solution works the first time, but it has a drawback: it always returns true even if 0 bytes are readable. A socket can be readable even if no data is available. This is not the behaviour I want, because I don't want to read when there is no data in the read buffer.

EDIT2 BIS:

Test this code:

public static boolean isReadable(Selector selector) {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if (!selectionKey.isValid()) {
                continue;
            }
            if (selectionKey.isReadable()) {
                    keyIterator.remove();
                return true;
            }
            keyIterator.remove();
        }
    } catch (IOException e) {
        System.err.println("Error!");
        e.printStackTrace();
    }
    return false;
}

public static void read() {
    try {
        Selector selector = Selector.open();
        SocketChannel socketChannel = SocketChannel.open();

        Socket socket = socketChannel.socket();

        System.out.println("Connecting to server...");
        socket.connect(new InetSocketAddress("localhost", 9999));
        System.out.println("Connected to server.");

        socketChannel.configureBlocking(false);

        socketChannel.register(selector, SelectionKey.OP_READ);

        ByteBuffer bb = ByteBuffer.allocate(1024);

        while (true) {
            boolean readable = isReadable(selector);
            if (readable) {
                System.out.println("Readable data found! Let's read...");
                bb.clear();
                int readBytes = socketChannel.read(bb);
                if (readBytes < 0) {
                    System.out.println("End of Stream found");
                    break;
                }
                byte[] readArray = new byte[readBytes];
                System.arraycopy(bb.array(), 0, readArray, 0, readBytes);
                System.out.println("Read (" + (readBytes) + " bytes): "
                        + new String(readArray, Charset.forName("UTF-8")));
                bb.flip();
                socketChannel.write(bb);
                Thread.sleep(1000);
            } else {
                System.out.println("No data to read, sleeping");
                Thread.sleep(1000);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    read();
}

First of all, I execute an application that listens for connections and writes data: I used netcat with this command: nc -l 9999. Then I execute my Java program. Finally I can write text lines in netcat and my Java program works perfectly.

Remember it is a proof of concept, not a way to real programming solution. In fact, it is a very bad practise. Thank you @EJP and @Markus!

Upvotes: 3

Markus A.
Markus A.

Reputation: 12742

The idea behind NIO is to provide a way to wait for events (e.g. "connection has readable data ready") from any one of multiple connections at the same time, which sounds like exactly what you are looking for. Take a look at java.nio.channels.Selector. You basically register the "readable" event of all the connections you have with this selector object and then call one of three select methods that will wait for an event on one of your connections:

  1. select() - blocks until an event is available (use if your program has nothing else to do)
  2. select(long timeout) - blocks until an event is available or a timeout happens (use if you want to conserve CPU load and increase network responsiveness if it's OK if your program slows down a bit)
  3. selectNow() - returns immediately (use if your program needs to keep running)

Then you use the selectedKeys method to grab a list of all connections that have an event waiting (for example, have data ready to be read). Then just iterate over this list and read from the connections with data only and ignore the other connections that aren't in the list as they have no data available.

This will allow you to check without blocking WHETHER data is available (is connection in the list or not), but not HOW MUCH data is available. But if you then do a read on the connection with data available, it will return immediately without blocking and return as much data as is available, if you've given it a big enough buffer. You can then choose to buffer this data somewhere and make the amount of data in the buffer available, but something tells me you don't really need this anyways and just want to go ahead and process the data.

Upvotes: 7

user207421
user207421

Reputation: 310850

Can't be done. An API is present, formally speaking, via SocketChannel.socket().getInputStream().available(), but getInputStream() operations will fail on a non-blocking channel, so it can't be used in your circumstance.

EDIT: Now that you've illuminated us a little, what you require still doesn't exist in Java, but as you are in non-blocking mode it doesn't matter in the slightest. Just read into a buffer that is at least as big as your socket receive buffer: the number of bytes read is the number of bytes that could have been read without blocking, which is exactly what you just did.

Upvotes: 7

Debobroto Das
Debobroto Das

Reputation: 862

Bytebuffer.limit will return you the length available in the buffer.

sctpChannel.receive(byteBuffer, null, null);
byteBuffer.flip();

 if (byteBuffer.limit() > 0) 
  {
         .....................//Do your work here
    //             read the data in a byte array
    byteBuffer.clear();  
  }

Upvotes: -3

irreputable
irreputable

Reputation: 45433

I'm not aware of any method in NIO that does that. One just go ahead and read.

Upvotes: 1

Related Questions