我想监控下载数据的进度。我想在传输了一定数量的数据后进行日志记录。我的代码:
int contentLength = 0;
final int bufferSize = 1024*8;
byte[] buffer = new byte[bufferSize];
int length = 0;
while ( (length = bufferedInputStream.read(buffer) ) !=-1 ) {
contentLength = contentLength+length;
if ( (contentLength % (bufferSize*1024*4)) ==0 ) {
logger.debug(contentLength);
}
}
这似乎不工作。似乎缓冲区并不总是满的,因此用作模的buffersize的倍数不匹配。
缓冲区不"满"真的很常见吗?这是怎么发生的?缓冲区被"刷新"的内部逻辑是什么?Java是否等待特定的时间来接收数据包,然后刷新(如果缓冲区未满)?任何内部工作原理的信息将有助于理解它。
(我不需要一个解决方案,我已经实现了它,只是想知道如果这是常见的,缓冲区从来没有完全读取?我很想知道为什么。)
非常感谢!Jens
在套接字上的读操作不会完全填满缓冲区是很常见的。发送端正在刷新不同长度的数据包。然后,它们通过应用程序、操作系统和网络层,这些层可能会将它们分割开来。典型的结果是部分缓冲区读取。
我通常调整读缓冲区的大小以匹配套接字的读缓冲区,它作为最大大小,但我从不依赖它每次都被填满。
另外,您应该注意,当您执行批量读取(到字节数组)时,使用BufferedInputStream
是低效的。它只是增加了从一个数组复制数据到另一个数组的开销。它也是上述碎片化的来源之一。
不能保证缓冲区已满。这些都是IO的细节。您必须使用read
的返回值来确定实际读取了多少数据。
当您可以使用read(byte[], ...)
API时,流将尝试填充缓冲区中分配的空间。但它不会总是填满它。当然,如果流中的内容用完,它无法填充整个空间。但也有其他原因。例如,流实现可能使用一些后台线程来获取数据。如果read调用向下传递到操作系统,它可能一次读取一个数据块。如果流被缓冲了,而缓冲区仍然有一些内容,它可能只返回缓冲区中剩余的内容。
这实际上取决于您使用的实际InputStream
,并归结为"操作系统如何处理read()
调用"。
在大多数现代操作系统上,基本的read
调用做同样的事情:它尝试读取请求的尽可能多的数据,但可能会提前停止。
当您的缓冲区大于文件系统的预读缓冲区时,很容易发生这种情况。或者当你正在从网络连接中读取,而只有几个数据包到达时。
一些设备具有相当的预测行为(从文件系统读取倾向于完全填满所提供的缓冲区,如果它不是很大,从网络读取则更经常地使其半满)。但是你不能以这样或那样的方式依赖它。
所以:是的,这很容易发生。