8

InputStream 类的 JavaDoc 说明如下:

从输入流中读取最多 len 个字节的数据到一个字节数组中。尝试读取多达 len 个字节,但可能会读取较小的字节数。实际读取的字节数以整数形式返回。在输入数据可用、检测到文件结尾或引发异常之前,此方法会一直阻塞。

这也符合我的经验。例如,请参见下面的示例代码:

Client:
Socket socket = new Socket("localhost", PORT);
OutputStream out = socket.getOutputStream();
byte[] b = { 0, 0 };
Thread.sleep(5000);
out.write(b);
Thread.sleep(5000);
out.write(b);

Server:
ServerSocket server = new ServerSocket(PORT);
Socket socket = server.accept();
InputStream in = socket.getInputStream();
byte[] buffer = new byte[4];
System.out.println(in.read(buffer));
System.out.println(in.read(buffer));

Output:
2   // Two bytes read five seconds after Client is started.
2   // Two bytes read ten seconds after Client is started.

对 read(buffer) 的第一次调用会阻塞,直到输入数据可用。然而,该方法在读取两个字节后返回,即使字节缓冲区中仍有空间,这与 JavaDoc 声明“尝试读取尽可能多的 len 个字节,但可能读取较小的数字”相对应。但是,当输入流来自套接字时,是否保证一旦读取了至少一个字节的数据,该方法就不会阻塞?

我问的原因是我在小型 Java Web 服务器NanoHTTPD中看到了以下代码,我想知道小于 8k 字节(大多数请求是)的 HTTP 请求是否可能使线程无限期阻塞,除非有保证它一旦读取一些数据就不会阻塞。

InputStream is = mySocket.getInputStream();
// Read the first 8192 bytes. The full header should fit in here.
byte[] buf = new byte[8192];
int rlen = is.read(buf, 0, bufsize);

编辑

让我尝试用一​​个相对相似的代码示例再次说明。EJP 表示该方法会阻塞,直到 EOS 发出信号或至少一个字节的数据已经到达,在这种情况下,它会读取已经到达的数据字节,而不会再次阻塞,并返回该数字,这对应于方法的JavaDoc类 InputStream 中的 read(byte[], int, int)。但是,如果实际查看源代码,则很明显该方法确实会阻塞,直到缓冲区已满。我已经通过使用与上面相同的客户端并将 InputStream 代码复制到我的服务器示例中的静态方法对其进行了测试。

public static void main(String[] args) throws Exception {
    ServerSocket server = new ServerSocket(PORT);
    Socket socket = server.accept();
    InputStream in = socket.getInputStream();
    byte[] buffer = new byte[4];
    System.out.println(read(in, buffer, 0, buffer.length));
}

public static int read(InputStream in, byte b[], int off, int len) throws IOException {
    if (b == null) {
        throw new NullPointerException();
    }
    else if (off < 0 || len < 0 || len > b.length - off) {
        throw new IndexOutOfBoundsException();
    }
    else if (len == 0) {
        return 0;
    }

    int c = in.read();
    if (c == -1) {
        return -1;
    }
    b[off] = (byte)c;

    int i = 1;
    try {
        for (; i < len; i++) {
            c = in.read();
            if (c == -1) {
                break;
            }
            b[off + i] = (byte)c;
        }
    }
    catch (IOException ee) {
    }
    return i;
}

此代码将作为其输出:

4   // Four bytes read ten seconds after Client is started.

现在显然 5 秒后有可用的数据,但是该方法仍然会阻止尝试填充整个缓冲区。Socket.getInputStream() 返回的输入流似乎并非如此,但它是否保证一旦数据可用它就永远不会阻塞,就像JavaDoc所说但不像源代码显示的那样?

4

1 回答 1

5

但是,当输入流来自套接字时,是否保证一旦读取了至少一个字节的数据,该方法就不会阻塞?

我认为这个问题没有任何意义。该方法阻塞,直到 EOS 发出信号或至少一个字节的数据已经到达,在这种情况下,它会读取已经到达的数据字节,而不会再次阻塞,并返回该数字。

我在小型 Java Web 服务器 NanoHTTPD 中看到了以下代码

代码是错误的。它使整个标头将在第一次读取时交付的假设无效。我希望在这里看到一个循环,直到检测到一个空行。

我想知道小于 8k 字节(大多数请求是)的 HTTP 请求是否可能使线程无限期阻塞,除非有保证一旦读取某些数据就不会阻塞。

我再次认为这没有任何意义。该方法将阻塞,直到至少一个字节到达,或 EOS。时期。

于 2012-11-06T09:30:14.647 回答