Java理解I/O流

3

对我而言,在编程中Java中的I/O流是最容易被误解的概念。

假设我们从套接字连接中获取输入流:

DataInputStream in = new DataInputStream(clientSocket.getInputStream());

当我从远程服务器获取数据时,以下哪种描述是正确的?

  1. Data stored in the in variable. When extra data comes from server, it appends to in, increasing its size. And then we can read data from in variable that way:

    byte[] messageByte = new byte[1000];
    boolean end = false;
    String dataString = "";
    while(!end)
    {
        bytesRead = in.read(messageByte);
        messageString += new String(messageByte, 0, bytesRead);
        if (messageString.length == 100)
        {
            end = true;
        }
    }
    
  2. in is only a link to the source of data and doesn't contain data itself. When we call in.read(messageByte) 1000 bytes copy from the socket to bytesRead?

    Alternatively, instead of a socket let's say we have stream connected to file on HDD. When we call in.read(messageByte) we read 1000 bytes from HDD, yes?

哪种方法是正确的?我倾向于认为是第二种方法,但如果是这样,那么在套接字案例中数据存储在哪里?当我们读取1000个字节时,远程服务器是否在等待,然后再发送额外的数据?还是来自服务器的数据存储在操作系统中的某个缓冲区中?


你可以将这里的流想象成数据物理源头的窗口。它本身并不存储数据,只是允许你读取它。 - Tim Biegeleisen
#2 是正确的情况。要读取的数据存储在操作系统套接字缓冲区中,直到您从Java流请求数据。 - Nayuki
1
你读过Java™教程 - I/O流吗? - Andreas
是的,我已经阅读了它。但是John Kugelman的回答让我完全理解了这个问题。 - igor_rb
2个回答

2
  1. 数据存储在变量中。

不是这样的。

当服务器发送额外的数据时,它会附加到in变量中,增加其大小。然后我们可以通过以下方式从in变量中读取数据:

byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
while(!end)
{
    bytesRead = in.read(messageByte);
    messageString += new String(messageByte, 0, bytesRead);
    if (messageString.length == 100)
    {
        end = true;
    }
}

不行,见下方。

  1. in只是数据源的链接,不包含数据本身。

正确。

当我们调用in.read(messageByte);时,1000字节会从套接字复制到bytesRead吗?

不是。它会阻塞直到:

  • 至少传输了一个字节,或者
  • 流结束了,或者
  • 抛出了异常,

以先发生的为准。请参阅Javadoc。

(而不是套接字,我们可以有连接到HDD上的文件的流,当我们调用in.read(messageByte)时,我们从HDD中读取1000个字节。是吗?)

不是。同上。

哪种方法是正确的?

两种都不对。从输入流中读取的正确方法是循环,直到你得到了所有你期望的数据,或者EOS或异常发生。你不能依赖于read()填充缓冲区。如果你需要这个,使用DataInputStream.readFully()

我倾向于第二种

那没有意义。你没有选择。 (1)和(2)不是编程范例,而是关于流如何实际工作的问题。如何编写代码的问题与此不同。

数据存储在套接字中吗?

其中一部分存储在内核中的套接字接收缓冲区中。大部分还没有到达。它们都不在“套接字”中。

或者远程服务器在我们读取1000个字节后等待,并再次发送额外的数据?

不是这样的。服务器通过其套接字发送缓冲区将数据发送到您的套接字接收缓冲区。你的读取和服务器的写入彼此非常解耦。

或者服务器的数据存储在操作系统的任何缓冲区中?

是的,套接字接收缓冲区。


1

这取决于流的类型。数据存储的位置因流而异。有些具有内部存储,有些从其他来源读取,还有一些两者都有。

  • FileInputStream从磁盘上读取文件,只有在你请求时才会进行读取。数据在磁盘上,并不在流中。

  • 套接字的InputStream从操作系统缓冲区中读取。当数据包到达时,操作系统会自动读取并缓存一小部分数据(比如64KB)。从流中读取数据会耗尽操作系统缓冲区。如果缓冲区为空,因为没有数据包到达,你的read调用会被阻塞。如果你不能快速地清空缓冲区,导致它被填满,那么操作系统会丢弃网络数据包,直到你释放一些空间为止。

  • ByteArrayOutputStream有一个内部的byte[]数组。当你向流中写入数据时,它会将你的写入存储在这个数组中。在这种情况下,流是具有内部存储的。

  • BufferedInputStream与另一个输入流相结合。当你从BufferedInputStream中读取数据时,它通常会从底层流中请求大块数据并将其存储在缓冲区中。随后的read请求将使用缓冲区中的数据来满足,而不是对底层流执行额外的I/O操作。其目标是通过发出较少的大块读取请求来最小化底层流接收的单个read请求数量。在这种情况下,流具有一些内部存储和一些外部读取的混合策略。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接