Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何确保通过套接字流接收到整个文件?_Java_Sockets_Networking - Fatal编程技术网

Java 如何确保通过套接字流接收到整个文件?

Java 如何确保通过套接字流接收到整个文件?,java,sockets,networking,Java,Sockets,Networking,好的,我正在制作一个Java程序,它有一个服务器和客户端,我正在从服务器向客户端发送一个Zip文件。我几乎要把文件寄下去了。但收到后我发现有些不一致。我的代码并不总是得到完整的存档。我猜它是在BufferedReader拥有全部内容之前终止的。以下是客户端的代码: public void run(String[] args) { try { clientSocket = new Socket("jacob-custom-pc", 4444);

好的,我正在制作一个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);
        }
    }

那么,在写入文件之前,我如何确保完整的归档文件在那里呢?

在发送端,在开始写入文件之前,先写入文件大小。在读取端读取文件大小,以便知道预期的字节数。然后给read打电话,直到你得到你想要的一切。使用网络套接字,可能需要多次调用才能读取所有发送的内容。当您的数据越来越大时,这一点尤其正确

HTTP发送一个
内容长度:x
+\n字节。这很优雅,如果连接断开,可能会引发TimeoutException。

您使用的是TCP套接字。ZIP文件可能比网络MTU大,因此它将被拆分为多个数据包,并在另一端重新组装。然而,类似的事情可能会发生:

  • 客户端连接
  • 服务器开始发送。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);
socketclientsocket=新插座(“jacob定制pc”,4444);
DataInputStream dataReader=新的DataInputStream(clientSocket.getInputStream());
FileOutputStream out=新的FileOutputStream(“out.zip”);
long size=dataReader.readLong();
长块=大小/1024;
int lastChunk=(int)(大小-(chunks*1024));
字节[]buf=新字节[1024];
for(长i=0;i
服务器使用
DataOutputStream
发送实际文件之前的文件大小。我没有对此进行测试,但它应该可以工作。

In.available()
只是告诉您,
In.read()
目前没有阻塞(等待)的数据,但这并不意味着流的结束。但是,它们可能会随TCP/IP数据包随时进入您的电脑。通常,您从不在.available()中使用
In.read()
足以完全读取流。读取输入流的模式是

byte[] buf;
int size;

while ((size = in.read(buf)) != -1)
  process(buf, size);

// end of stream has reached
这样,您将完全读取流,直到其结束

更新如果要读取多个文件,则将流分块到“数据包”中,并在每个数据包前面加上整数大小的前缀。然后读取,直到收到大小字节,而不是in.read=-1

update2无论如何,永远不要使用.available中的
在数据块之间进行去标记。如果这样做,则意味着传入数据段之间存在时间延迟。您只能在实时系统中执行此操作。但是Windows、Java和TCP/IP都是与实时性不兼容的层

如何确保通过套接字流接收到整个文件

通过修正你的代码。您正在使用
InputStream.available()
作为流结束的测试。这不是它的目的。将复制循环更改为此,这也简单得多:

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

用于任何大于零的缓冲区大小,通常为8192。

这个问题与HTTP无关。我的答案不是“使用HTTP”,我的答案是:HTTP使用优雅的解决方案解决这个众所周知的问题。也就是说,你的答案是你说你的答案是注释而不是答案?什么是MTU。TCP表示存在连接。你的意思是,连接被超时关闭,并且这被报告为发送方已顺利关闭套接字?没有超时,你只是将代码设计为在第一批数据到达后停止接收数据。另外,请不要误解我,我不想侮辱你,但是如果你不知道你正在处理的东西是如何工作的,因此也不知道你在做什么,那么开发网络代码是一个坏主意。理解什么是TCP/IP是很好的,但我认为Java API在这里就足够了。直到
size=read(buf)
返回size=-1为止。这就是这条河的尽头。无需过度复杂化。您知道,当您使用Java streams时,Unix可能很重要。我是否应该学习Unix streams来进行Java I/O?如果TCP是一个可靠的协议,并且它告诉您endofstream,以便您可以自己确定大小(实际上您并不需要它),那么为什么需要文件大小?只有在写入文件后关闭套接字时@Val End of stream才起作用。对于他的简单应用程序来说,它可能已经足够好了。在通过一个连接发送多个内容的一般情况下,您需要一种不同的方法来完成。很好,我最终使用了这种方法。它执行do while循环,直到读取的总字节数等于提前发送的总字节数。多亏了梅因和瓦尔的帮助。你们在这方面比我强多了如果它认为流已完成,但有更多的传入字节,该怎么办?@Jadar End of stream意味着套接字已关闭,因此不会有更多的传入字节。唯一真正的缺点是,由于连接丢失,客户端无法区分成功与错误。如果我想让套接字保持打开状态以进行更多传输,该怎么办?然后你将tarnsfer分块,