2

I'm reading Socket InputStream, calling read() and available() works for few looping iterations. Later available() blocks indefinitely!

What could be the issue? How can I make this non-blocking?

Code:

BufferedInputStream buffIn = new BufferedInputStream(in);
while (true)
{
    if (buffIn.available() > 0)
    {
        len = buffIn.read(buffer, 0, buffer.length);
        if (len == -1)
        {
            break;
        }
        baos.write(buffer, 0, len);
    }
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
Praveen
  • 87
  • 2
  • 2
  • 7
  • I suggest you try NIO, and [this](http://stackoverflow.com/questions/1605332/java-nio-filechannel-versus-fileoutputstream-performance-usefulness?rq=1) question should probably help with that. – Elliott Frisch Dec 12 '13 at 19:20
  • Thanks. Are you suggesting there is no other way than NIO? – Praveen Dec 12 '13 at 19:24
  • Are you certain it's `available()` which is blocking? I didn't think it was supposed to ever block, but it's been a while since I used the Java IO APIs so I might be mistaken. – TypeIA Dec 12 '13 at 19:24
  • The stack trace is as below (suggests available() is waiting)-
    
    PlainSocketImpl.socketAvailable() line: not available [native method]
    SocksSocketImpl(PlainSocketImpl).available() line: 472
    SocketInputStream.available() line: 217
    BufferedInputStream.available() line: 381
    
    – Praveen Dec 12 '13 at 19:30

3 Answers3

4

available() is not a good idea for Sockets as it doesn't work as expected. I would use non-blocking NIO in this place.

SocketChannel sc = ...
sc.configureBlocking(false);
ByteBuffer bb = ByteBuffer.allocateDirect(32*1024);
while(sc.read(bb) > 0) {
    bb.flip();
    while(bb.remaining() > 0 && sc.write(bb) >= 0);
    bb.clear();
}

This is quite a bit more efficient than the IO version as it doesn't copy the data into the Java scape just to copy it back out (it saves two copies)

EDIT The canonical version of the loop is follows:

while (in.read(bb) > 0 || bb.position() > 0)
{
    bb.flip();
    out.write(bb);
    bb.compact();
}
user207421
  • 305,947
  • 44
  • 307
  • 483
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    Peter, I edited in a correct version of the loop. You might want to lose your version. It can lose data at the end. You're assuming that write writes everything. – user207421 Dec 12 '13 at 22:56
  • @EJP The write is in the loop until there is no more data or the connection is lost. You loop is better as it doesn't block on write when it could be reading. – Peter Lawrey Dec 12 '13 at 23:06
3

It is not blocking it is spinning.

Once there is no data available you code might as well read

while (true)
{
    if (buffIn.available() > 0) // ALWAYS false now we've run out of data
    {
       // unreachable
    }
}

The loop will never finish. Your test for the minus 1 value will never be executed when there is no data available.

You are seeing available() in the stack trace because that's about the only thing in your loop that's taking up any time, so the chances are, when you create you stack trace, that's where it's going to be.

If you are confident that you will infact get an end-of-file condition (eg if it's TCP and the other end closes the connection) then the available() call is not required at all. Otherwise, you need a different method of determining you have all of the data. For example is the payload size encoded in the first few bytes or something?

Rodney
  • 2,642
  • 1
  • 14
  • 15
1

Your code has a pretty big bug; you never did anything with the available result; so your read blocks.

if (buffIn.available() > 0)
{
    int amt = (buffIn.available() > buffer.length) ? buffer.length : 
        buffIn.available();
    len = buffIn.read(buffer, 0, amt); // <-- see ternary above.
    if (len == -1)
    {
        break;
    }
    baos.write(buffer, 0, len);
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249