好的,所以我正在制作一个有服务器和客户端的Java程序,我正在从服务器向客户端发送一个Zip文件。我几乎把文件发送下来了。但接受我发现有些不一致。我的代码并不总是获得完整的存档。我猜它是在BufferedReader完成之前终止的。这是客户端的代码:
public void run(String[] args) {
try {
clientSocket = new Socket("jacob-custom-pc", 4444);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedInputStream(clientSocket.getInputStream());
BufferedReader inRead = new BufferedReader(new InputStreamReader(in));
int size = 0;
while(true) {
if(in.available() > 0) {
byte[] array = new byte[in.available()];
in.read(array);
System.out.println(array.length);
System.out.println("recieved file!");
FileOutputStream fileOut = new FileOutputStream("out.zip");
fileOut.write(array);
fileOut.close();
break;
}
}
}
} catch(IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
那么在编写文件之前如何确定完整存档?
答案 0 :(得分:2)
在开始编写文件之前,在发送方写入文件大小。在读取端读取文件大小,以便知道预期的字节数。然后打电话给读,直到你得到你所期望的一切。使用网络套接字,可能需要多次调用才能获取发送的所有内容。当您的数据变大时尤其如此。
答案 1 :(得分:0)
HTTP以字节为单位发送content-length: x
+ \ n。这很优雅,如果conn被破坏,它可能会抛出TimeoutException。
答案 2 :(得分:0)
您正在使用TCP套接字。 ZIP文件可能比网络MTU大,因此它将被分成多个数据包并在另一侧重新组装。不过,这样的事情可能会发生:
while (true)
,直到收到第一个数据包。in.available() > 0
)正如您所看到的:除非客户端计算机速度很慢并且网络速度非常快并且具有巨大的MTU,否则您的代码将无法接收整个文件按设计。这就是你构建它的方式。
另一种方法:使用长度预先填充数据。
Socket clientSocket = new Socket("jacob-custom-pc", 4444);
DataInputStream dataReader = new DataInputStream(clientSocket.getInputStream());
FileOutputStream out = new FileOutputStream("out.zip");
long size = dataReader.readLong();
long chunks = size / 1024;
int lastChunk = (int)(size - (chunks * 1024));
byte[] buf = new byte[1024];
for (long i = 0; i < chunks; i++) {
dataReader.read(buf);
out.write(buf);
}
dataReader.read(buf, 0, lastChunk);
out.write(buf, 0, lastChunk);
服务器使用DataOutputStream
在实际文件之前发送文件大小。我没有对此进行测试,但它应该有效。
答案 3 :(得分:0)
如何确保通过套接字流收到整个文件?
修复你的代码。您正在使用InputStream.available()
作为流结束的测试。这不是它的用途。将您的副本循环更改为此,这也简化了很多:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
使用任何大于零的缓冲区大小,通常为8192。
答案 4 :(得分:-1)
发送时在文件末尾添加终止字符。我相信Unix使用-1来表示文件的结尾。收到文件后,请等到找到-1然后将其删除。
答案 5 :(得分:-1)
In.available()
只是告诉您in.read()
目前没有阻止(等待)的数据,但这并不意味着流的结束。但是,他们可能随时使用TCP / IP数据包进入您的PC。通常,您从不使用in.available()
。 In.read()
足以完全读取流的所有内容。读取输入流的模式是
byte[] buf;
int size;
while ((size = in.read(buf)) != -1)
process(buf, size);
// end of stream has reached
这样,您将完全读取流,直到结束。
更新如果您想要读取多个文件,那么您将流式传输到&#34;数据包&#34;并以每个整数大小为前缀。然后读取直到收到大小字节而不是in.read = -1
。
update2 无论如何,永远不要使用in.available
来区分数据块。如果这样做,则表示传入数据之间存在时间延迟。您只能在实时系统中执行此操作。但Windows,Java和TCP / IP都是这些层与实时不兼容的。