【问题标题】:Sending multiple files - end of each file in the byte[] buffer发送多个文件 - byte[] 缓冲区中每个文件的结尾
【发布时间】:2016-12-22 19:10:27
【问题描述】:

我在两个设备之间发送文件,所以我建立了一个套接字通信。现在,我只是想发送一个文件,但将来我想发送多个文件(由用户从网格视图中选择)。

问题是当我发送一个文件时,在服务器端(接收文件)socket.getInputStream().read(buffer) 没有检测到文件的结尾。它只是等待发送“更多”数据。

在对这个问题进行了一些搜索之后,我找到了一些主题,这些主题给了我一些选项,但我仍然不满意,因为我不知道这些选项是否能有效地发送多个文件。这是一个例子:How to identify end of InputStream in java

我可以在发送文件后关闭套接字或流对象,但是如果我想发送大量文件,总是关闭和打开套接字会效率不高。

接收器上的代码:

  File apkReceived = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/testeReceiveServerComm.apk");
  byte[] buffer = new byte [8192];
  FileOutputStream fos=new FileOutputStream(apkReceived);
  int count=0;
  int total=0;//so apra ir vendo quanto recebi.
  while((count = in.read(buffer)) != -1){
        fos.write(buffer,0,count);
        total+=count;
        System.out.println("Server Comm receive thread - already received this ammount : "+total);

  }

客户端(发送者)上的代码:

  File apkToSend=new File(filePath);
  byte[] buffer = new byte [8192];
  BufferedInputStream bis=new BufferedInputStream(new FileInputStream(apkToSend));
  int count;
  int total=0; 
  while((count=bis.read(buffer))!=-1){
         out.write(buffer,0,count);
         total+=count;
         out.reset();
         System.out.println("send thread - already sent this ammount : "+total);
            }

            out.flush();
            bis.close();

【问题讨论】:

  • 不要依赖流的结尾。它简单得多。只需先发送文件的大小。您可以将大小放入一个整数变量中,然后发送整数的四个字节。接收器首先读取四个字节并知道大小。然后它将从流中准确读取 size 字节。完成后,它再次读取下一个文件大小的四个字节。当所有四个字节都为零时,就没有更多的文件了。
  • 最简单的方法是使用例如protocol buffers 1) 写入文件“描述符”(路径、文件名、描述、文件大小等)并根据filesize 写入确切的字节数,然后转到 1),阅读器对其输入流执行相同的操作
  • 感谢两位。 Greenapps 方法类似于 Riyaz Parasara 写的答案,所以我实现了类似的东西。 Psink 非常感谢您的回答,肯定会检查协议缓冲区,也许它会更有效(我只会读取 1 次以获得大小和文件?)。再次感谢你们的回答

标签: android sockets streaming file-transfer android-networking


【解决方案1】:

您正在读取套接字,直到 read() 返回 -1。这是流结束条件 (EOS)。 EOS 在对等方关闭连接时发生。当它完成一个文件的写入时不是。

您需要在每个文件之前发送文件大小。您已经对文件计数做了类似的事情。然后确保您读取了该文件的确切字节数:

这是示例代码

String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize)) != -1)
{
  fos.write(buf,0,n);
  fileSize -= n;
}
fos.close();

您可以将所有这些包含在一个循环中,该循环在 readUTF() 抛出 EOFException 时终止。相反,在发送数据之前,您当然必须在发送方调用 writeUTF(filename) 和 writeLong(filesize)。

【讨论】:

  • 感谢您的回答。关于循环,我总是听说我应该把它放在一段时间内(!serverSocket.isClosed())......但最近在stackoverflow上有人告诉我这很糟糕。觉得不好吗?再次感谢您!
  • 不,它还不错,技术上和理论上我的答案都是正确的
  • 这不仅不好,而且毫无意义。 isClosed() 成为 true 的唯一方法是如果 关闭套接字,并且您不会在复制循环中这样做。
猜你喜欢
  • 2018-09-20
  • 1970-01-01
  • 1970-01-01
  • 2022-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多