views:

115

answers:

6

The problem I am having is that when I use an InputStream to read bytes, it blocks until the connection is finished. EG:

        InputStream is = socket.getInputStream();
        byte[] buffer = new byte[20000];
        while (is.read(buffer) != -1) {
            System.out.println("reading");
        }
        System.out.println("socket read");

"socket read" doesn't print out until the FYN packet is actually recieved, thus closing the connection. What is the proper way to receive all the bytes in without blocking and waiting for the connection to drop?

+2  A: 

Take a look at java.nio which has non-blocking IO support.

marklai
this sounds like a major overkill at this point. I think he should first master blocking io before even considering non blocking io.
Omry
+1  A: 

Example taken from exampledepot on java.nio

// Create a direct buffer to get bytes from socket.
// Direct buffers should be long-lived and be reused as much as possible.
ByteBuffer buf = ByteBuffer.allocateDirect(1024);

try {
    // Clear the buffer and read bytes from socket
    buf.clear();
    int numBytesRead = socketChannel.read(buf);

    if (numBytesRead == -1) {
        // No more bytes can be read from the channel
        socketChannel.close();
    } else {
        // To read the bytes, flip the buffer
        buf.flip();

        // Read the bytes from the buffer ...;
        // see Getting Bytes from a ByteBuffer
    }
} catch (IOException e) {
    // Connection may have been closed
}

Be sure to understand buffer flipping because it causes a lot of headache. Basically, you have to reverse your buffer to read from it. If you are to reuse that buffer to have the socket to write in it, you have to flip it again. However clear() resets the buffer direction.

Eric
Follow-up: is there a way to get blocking in this? I want to block until it connects, but using while(!sChannel.finishedConnect()) costs a lot of cpu cycles.
Zombies
Select on OP_CONNECT and check finishConnect() when you get it, or else just connect in blocking mode.
EJP
+2  A: 

With traditional sockets the point is that usually you do want them to block: what you do when logically you don't want your program to block is you put your reading/writing code in another thread, so that the separate read/write thread blocks, but not your whole program.

Failing that, you can use the available() method to see if there is actually any input available before reading. But then you need to be careful not to sit in a loop burning CPU by constantly calling available().

Edit: if the problem is that you're happy to block until the bytes have arrived, but not until the connection has dropped (and that is what is happeningh), then you need to make the client at the other end call flush() on its output stream after it has sent the bytes.

Neil Coffey
Yes, correct about that last point burning CPU. Is there an opitmized way that doesn't hurt me with cpu cycles or context switching (Does Thread.sleep(long) + polling cause a bad performance lost?
Zombies
Calling Thread.sleep() will cause some performence loss for the actual I/O (e.g. you could sleep for 500 millis but the data is ready after 100 millis, and you don't receive it for another 400). In terms of context switching, a few extra C/S per second don't really matter. But I'm still not clear: why can't you just let the socket block (in its own thread if necessary)?
Neil Coffey
Sorry, I do in fact want it to block the first go-round, read the bytes, and my problem was my unfamiliarity with java IO since I was calling read again, then I had to wait for EOS.
Zombies
+2  A: 

Reading till you get -1 means that you want to read until EOS. If you don't want to read until EOS, don't loop till the -1: stop sooner. The question is 'when?'

If you want to read a complete 'message' and no more, you must send the message in such a way that the reader can find its end: for example, a type-length-value protocol, or more simply a size word before each message, or a self-describing protocol such as XML.

EJP
+1  A: 

Try this:

        InputStream is = socket.getInputStream();
        byte[] buffer = new byte[20000];
        int bytesRead;
        do {
            System.out.println("reading");
            bytesRead = is.read(buffer);
        }
        while (is.available() > 0 && bytesRead != -1);
        System.out.println("socket read");

More info: http://java.sun.com/j2se/1.4.2/docs/api/java/io/InputStream.html#available()

Antonio
Good answer, welcome to SO.
Zombies
when you figured out it does not always work, revisit my answer.
Omry
Shouldn't the call to `is.available()` happen before the `is.read()` call, considering that `is.read()` might block if no bytes are available on the first read?
Martin Wickman
I do want it to block until bytes are available the first go round. I find this to be most optimized since it doesn't consume CPU resources and it doesn't poll. Also, this code works pretty fine for me, tested it this morning. If I run into problems I will update.
Zombies
+1  A: 

the code is probably not doing what you think it does. read(buffer) returns the number of bytes it read, in other words: it is not guaranties to fill up your buffer anyway. See DataInputStream.readFully() for code that fill up the entire array:

or you can use this functions (which are based on DataInputStream.readFully()) :

public final void readFully(InputStream in, byte b[]) throws IOException
{
    readFully(in, b, 0, b.length);
}

public final void readFully(InputStream in, byte b[], int off, int len) throws IOException
{
    if (len < 0) throw new IndexOutOfBoundsException();
    int n = 0;
    while (n < len)
    {
        int count = in.read(b, off + n, len - n);
        if (count < 0) throw new EOFException();
        n += count;
    }
}

Your code would look like:

InputStream is = socket.getInputStream();
byte[] buffer = new byte[20000];
readFully(is, buffer);
System.out.println("socket read");
Omry