首页 > 解决方案 > Why does InputStream.read() throw "Read timed out" exception when the inputStream object has previously been wrapped in a BufferedReader?

问题描述

I have an inputStream object that comes from a socket.

This stream could be all plain text, or it could contain binary data. It has a plain text "prefix" that tells me which type of data it has. I use BufferedReader to read that prefix. Then, if the data type is plain text, I continue to use BufferedReader to read the rest which works just fine.

The problem is when I switch back to using InputStream if the prefix indicates non-text data. The stream seems to be lost somewhere, and I get a java.net.SocketTimeoutException: Read timed out instead.

Code goes something like this:

InputStream instream = mysocket.getInputStream();
BufferedReader br = (new InputStreamReader(instream, "UTF-8")));

byte[] prefixArray = new byte[4];
br.read(prefixArray, 0, 4);
String prefix = new String(prefixArray);

if("text".equals(prefix)) {
   // continue to use br.read() here, which works fine...
}else{
   byte[] barray = new byte[arraysize]
   instream.read(barray, 0, arraysize); // THROWS "Read timed out" exception
}

Is this simply not allowed? Once instream is wrapped in a BufferedReader, it can't be used directly any longer?

标签: javasocketsinputstreambufferedreaderjava-io

解决方案


A BufferedReader reads from a Reader and stores the data in a buffer for subsequent reads to use. It reads as much as it can at one time, and each time the buffer is emptied, it reads more into the buffer.

Your binary reads time out because the BufferedReader has already read the bytes you are trying to read.

It takes only 4 bytes for you to detect the stream type. Using a Reader for those 4 bytes is overkill. Read the bytes directly from the InputStream first, then create a Reader only for the text data, eg:

InputStream instream = mysocket.getInputStream();

byte[] prefixArray = new byte[4];
int offset = 0;
int numread;
do
{
    numread = instream.read(prefixArray, offset, 4-offset);
    if (numread == -1) return;
    offset += numread;
}
while (offset < 4);
String prefix = new String(prefixArray);

if ("text".equals(prefix))
{
    BufferedReader br = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
    //...
}
else
{
    byte[] barray = new byte[arraysize];
    // consider using BufferedInputStream here ...
    do
    {
        numread = instream.read(barray, 0, arraysize);
        if (numread == -1) break;
        //...
    }
    while (true);
}

Alternatively, consider using BufferedInputStream for the socket reading, and add other classes on top of it as needed, eg:

InputStream instream = mysocket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(instream);

byte[] prefixArray = new byte[4];
DataInputStream dis = new DataInputStream(bis);
dis.readFully(prefixArray);
String prefix = new String(prefixArray);

if ("text".equals(prefix))
{
    InputStreamReader isr = new InputStreamReader(bis, "UTF-8"));
    //...
}
else
{
    byte[] barray = new byte[arraysize];
    do
    {
        numread = bis.read(barray, 0, arraysize);
        if (numread == -1) break;
        //...
    }
    while (true);
}

推荐阅读