【问题标题】:File transfer over java sockets using multiple IO streams使用多个 IO 流通过 java 套接字传输文件
【发布时间】:2013-07-15 19:27:23
【问题描述】:

最近,我编写了一个简单的客户端服务器程序,用于通过标准 TCP 套接字传输文件。 WiFi 通道的平均吞吐量约为 2.2Mbps。我的问题是: 是否可以通过多个数据 IO 流传输一个大文件(比如 5 GB),以便每个流可以并行传输同一文件的多个部分(不同的线程可以用于此目的)?这些文件部分可以在接收端重新组装。 我试图拆分一个小文件并将其传输到数据输出流上。第一段工作正常,但我不知道如何以选择性方式读取文件输入流(我也尝试了 mark() 和 reset() 方法进行选择性读取但没有用)

这是我的代码(出于测试目的,我已将输出重定向到 fileoutputstream):

    public static void main(String[] args) {
    // TODO Auto-generated method stub
    final File myFile=new File("/home/evinish/Documents/Android/testPicture.jpg");
    long N=myFile.length();
    try {
        FileInputStream in=new FileInputStream(myFile);
        FileOutputStream f0=new FileOutputStream("/home/evinish/Documents/Android/File1.jpg");
        FileOutputStream f1=new FileOutputStream("/home/evinish/Documents/Android/File2.jpg");
        FileOutputStream f2=new FileOutputStream("/home/evinish/Documents/Android/File3.jpg");

        byte[] buffer=new byte[4096];
        int i=1, noofbytes;
        long acc=0;
        while(acc<=(N/3)) {
            noofbytes=in.read(buffer, 0, 4096);
            f0.write(buffer, 0, noofbytes);
            acc=i*noofbytes;
            i++;
        }
        f0.close();

我得到了文件的第一段(这可以在一个线程中复制到 DataOutputStream)。任何人都可以建议,如何在 N/3 段中读取文件的剩余部分(N/3 字节之后),以便三个流可以在三个线程中用于并发操作?

这是在接收端合并文件段的代码:

    package com.mergefilespackage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MergeFiles {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        IOCopier.joinFiles(new File("/home/evinish/Documents/Android/File1.jpg"), new File[] {
            new File("/home/evinish/Documents/Android/File2.jpg"), new File("/home/evinish/Documents/Android/File3.jpg")});
    }
}
class IOCopier {
    public static void joinFiles(File destination, File[] sources)
            throws IOException {
        OutputStream output = null;
        try {
            output = createAppendableStream(destination);
            for (File source : sources) {
                appendFile(output, source);
            }
        } finally {
            IOUtils.closeQuietly(output);
        }
    }

    private static BufferedOutputStream createAppendableStream(File destination)
            throws FileNotFoundException {
        return new BufferedOutputStream(new FileOutputStream(destination, true));
    }

    private static void appendFile(OutputStream output, File source)
            throws IOException {
        InputStream input = null;
        try {
            input = new BufferedInputStream(new FileInputStream(source));
            IOUtils.copy(input, output);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }
}
class IOUtils {
    private static final int BUFFER_SIZE = 1024 * 4;

    public static long copy(InputStream input, OutputStream output)
            throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    public static void closeQuietly(Closeable output) {
        try {
            if (output != null) {
                output.close();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

任何帮助将不胜感激!提前致谢!

【问题讨论】:

  • 如果您尝试通过同一个 WiFi 通道传输更多数据,它可能会变得更慢,因为每个套接字都会增加对其他套接字的争用。
  • +1 到@hexafraction。问题是什么是限制因素。我怀疑(如@hex)您受到 wifi 速度的限制,并且由于其他连接将在同一个 wifi 通道上,它们将无法并行工作。
  • @Gray 作为答案发布。
  • 嗯,这令人失望.. 我是套接字编程的新手,所以只是想探索一下。顺便说一句,感谢您提供宝贵的信息!你能推荐一些通过java套接字传输文件的优化技术吗?用dataoutputstream代替文件通道可以吗?
  • @VinitShandilya 你的限制仍然在链接上(假设你使用BufferedWriters

标签: java multithreading sockets file-transfer large-files


【解决方案1】:

您无法通过具有更多套接字的同一链接获得更多速度。每个套接字发送一定数量的数据包,每个数据包都有一定的大小。当我们将套接字数量增加一倍时,每秒数据包数量*套接字减少了一半,然后由于冲突、开销和争用而减少甚至更多。数据包开始碰撞、混乱和恐慌。操作系统无法处理丢失 ACK 的混乱局面,WiFi 卡难以以这样的速率传输。它也正在失去其低级 ack。随着数据包丢失,绝望的 TCP 堆栈会降低传输速率。如果由于信号改善而能够出现这种情况,那么它现在由于愚蠢的窗口综合症或其他形式的 TCP 死锁而陷入较低的速度。

任何通过 WiFi 尝试从更宽的载波频段、MiMo 或多条路径中获得更高速度的尝试都已经实现了收益,即使使用一个插槽也是如此。你不能再继续下去了。

现在,等等。我们的速度远远低于 WiFi 速度,不是吗?当然,我们需要使用缓冲!

确保从套接字的 getInputStream 或 getOutputStream 方法创建 BufferedWriter 和 BufferedReader 对象。然后写入/读取这些缓冲区。您的速度可能会有所提高。

【讨论】:

  • +1 由于WIFI通道吞吐量的限制,每个socket的吞吐量减半。
【解决方案2】:

您可以获取FileInputStream 的字节数组并将其拆分为每 10 KB(每 10.000 个字节)。 然后按顺序通过流发送这些部分。

在服务器上,您可以再次将数组放在一起并从这个巨大的字节数组中读取文件。

【讨论】:

  • 10KB != 10000字节
  • 这不会导致更高的吞吐量,只是更复杂的代码。
  • 嗯...然后每10240字节拆分一次
  • @BackSlash 10KB=10000 字节。 10KiB=10240 字节。这个数字是有效的,但仍然不会有更高的吞吐量。
猜你喜欢
  • 1970-01-01
  • 2020-12-02
  • 2018-08-19
相关资源
最近更新 更多