java FileInputStream和DataOutputStream处理字节[]缓冲区
我一直在开发一个在两台主机之间移动文件的应用程序,当我让传输过程正常工作时(代码仍然很混乱,很抱歉,我还在修复它),我有点想知道它到底是如何处理缓冲区的。我对java的网络相当陌生,所以我不想以“嗯,我成功了,让我们继续”的态度结束
文件发送代码
public void sendFile(String filepath, DataOutputStream dos) throws Exception{
if (new File(filepath).isFile()&&dos!=null){
long size = new File(filepath).length();
String strsize = Long.toString(size) +"\n";
//System.out.println("File size in bytes: " + strsize);
outToClient.writeBytes(strsize);
FileInputStream fis = new FileInputStream(filepath);
byte[] filebuffer = new byte[8192];
while(fis.read(filebuffer) > 0){
dos.write(filebuffer);
dos.flush();
}
文件接收代码
public void saveFile() throws Exception{
String size = inFromServer.readLine();
long longsize = Long.parseLong(size);
//System.out.println(longsize);
String tmppath = currentpath + "\\" + tmpdownloadname;
DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
FileOutputStream fos = new FileOutputStream(tmppath);
byte[] filebuffer = new byte[8192];
int read = 0;
int remaining = (int)longsize;
while((read = dis.read(filebuffer, 0, Math.min(filebuffer.length, remaining))) > 0){
//System.out.println(Math.min(filebuffer.length, remaining));
//System.out.println(read);
//System.out.println(remaining);
remaining -= read;
fos.write(filebuffer,0, read);
}
}
我想知道如何准确地处理两侧的缓冲区,以避免写入错误的字节。(ik接收代码如何避免这种情况,但我仍然想知道字节数组是如何处理的)
fis/dis是否总是等待缓冲区完全填满?在接收代码时,如果小于filebuffer,它总是写入完整数组或剩余长度。但发送代码的fis的长度如何
# 1 楼答案
事实上,你的代码可能有一个微妙的错误,正是因为你处理缓冲区的方式
从原始文件读取缓冲区时,
read(byte[])
方法返回实际读取的字节数。实际上,无法保证所有8192字节都已被读取假设你有一个10000字节的文件。第一次读取操作读取8192字节。但是,第二次读取操作将只读取1808字节。第三个操作将返回-1
在第一次读取时,您会准确地写入已读取的字节,因为您读取的缓冲区已满。但在第二次读取中,缓冲区实际上包含1808个正确的字节,而剩余的6384个字节是错误的——它们仍然存在于前一次读取中
在这种情况下,您是幸运的,因为这只发生在您编写的最后一个缓冲区中。因此,当达到预先发送的长度时,您停止在客户端读取,这会导致您跳过那些本来不应该发送的6384个错误字节
但事实上,即使尚未到达结尾,也无法保证从文件中读取将返回8192字节。该方法的契约不能保证这一点,这取决于操作系统和底层文件系统。例如,它可以在第一次读取时发送5000字节,在第二次读取时发送5000字节。在这种情况下,您将在文件中间发送3192个错误字节。
因此,您的代码实际上应该如下所示:
与接收端的代码非常相似。这保证了只写入实际读取的字节
所以缓冲区的处理方式并没有什么神奇之处。给流一个缓冲区,告诉它允许填充多少缓冲区,但不能保证它会填充所有缓冲区。它可能会填充得更少,而你必须小心使用它告诉你它填充的部分
不过,您正在犯的另一个严重错误是,将您收到的
long
转换为这一行中的int
:文件可能比整数包含的长度长。尤其是长视频等。这就是为什么你首先会得到一个的范围内)
long
的数字。不要那样截断它。保持remaining
为long
,并在取最小值后将其更改为int
(因为你知道最小值总是在^{顺便说一句,没有真正的理由为此使用
DataOutputStream
和DataInputStream
。read(byte[])
、read(byte[],int,int)
、write(byte[])
和write(byte[],int,int)
是从底层InputStream
继承的,没有理由不直接使用套接字的OutputStream
/InputStream
,或者使用BufferedOutputStream
/BufferedOutputStream
来包装它。在完成写作/阅读之前,也不需要使用flush
此外,在完成文件输入/输出流时,不要忘记至少关闭它们。您可能希望保持套接字输入/输出流处于打开状态,以便继续通信,但不需要保持文件本身处于打开状态,这可能会导致问题。利用资源进行尝试,以确保它们已关闭