使文件传输更高效Java

时间:2011-09-21 12:51:54

标签: java sockets networking nio file-transfer

我有两台无线电脑连接到N无线路由器。这些PC中的每一台都以108-150Mbps的速度连接。

理论上,我应该能够在绝对最佳的条件下以13.5MB / s的速度传输到18.75MB / s。

第一台计算机(正在发送)使用速度非常快的SSD,如果我没记错的话,大约100MB / s。 CPU使用率也低于20%。

它在656367ms内发送了1960273535字节(1.8GB)。那是2.8MB / s(108兆比特中的22个)。 当我打开任务管理器时,我发现只有25-27%的网络连接正在使用。

我正在寻找能够更快(通过网络)传输的任何想法,建议或改进。我想在一个线程上从磁盘缓冲文件并从另一个线程发送缓冲数据,但我不确定这是不是一个好主意。 这是SSCCE:

主机:

import java.io.*;
import java.net.*;


public class Host {


    public static void main(String[] args) throws IOException {


        ServerSocket servsock = new ServerSocket(15064);
        Socket sock = servsock.accept();
            long time = System.currentTimeMillis();

        OutputStream out = sock.getOutputStream();
        FileInputStream fileInputStream = new FileInputStream("C:\\complete.rar");

        byte [] buffer = new byte[64*1024]; 
        int bytesRead = 0;
        long totalSent = 0;

        while ( (bytesRead = fileInputStream.read(buffer)) != -1)
        {
            if (bytesRead > 0)
            {   
                out.write(buffer, 0, bytesRead);
                totalSent += bytesRead;
                System.out.println("sent " + totalSent);
            }   
        }

        sock.close();

        System.out.println("Sent " + totalSent + " bytes in "
                + (System.currentTimeMillis() - time) + "ms.");

    }
}

客户端:

import java.io.*;
import java.net.*;

public class Client {

    public static void main(String[] args) throws Exception {
        Socket sock = new Socket("127.0.0.1", 15064);
        InputStream in = sock.getInputStream();
        FileOutputStream fileOutputStream = new FileOutputStream("output.rar");

        byte [] buffer = new byte[64*1024]; 
        int bytesRead = 0;

        while ( (bytesRead = in.read(buffer)) != -1)
            fileOutputStream.write(buffer, 0, bytesRead);
        sock.close();
        fileOutputStream.close();
    }
}

编辑:我尝试映射网络驱动器并通过它发送文件,而Windows更糟糕 - 2.35MB / s。根据这篇文章http://tinyurl.com/634qaqg映射网络驱动器比FTP快,我也没有时间继续玩游戏并设置FTP服务器。

Edit2:更改定时器后,结果显示它通过WiFi以3MB / s的速度传输。我讨厌“理论”吞吐量。当我买东西时,我想知道它的真实表现。 事实证明,代码确实受到WiFi速度的限制。我仍然愿意接受建议。

编辑3:在100Mbps LAN上运行程序后,它设法以11.8MB / s的速度传输文件。考虑到最大传输速率为12.5MB / s,这是非常好的。

5 个答案:

答案 0 :(得分:6)

速度为2.8MB / s时,速度不太可能与您的代码有关。几乎可以肯定,由于无线网络无法达到理论吞吐量(可能是由于环境条件)。

很容易测试是否是这种情况:只需在同一台计算机之间进行大量ftpscp文件传输,并查看您所看到的吞吐量类型。

答案 1 :(得分:5)

我建议您尝试以下代码打印

Wed Oct 26 14:21:03 BST 2011: Accepted a connection
Wed Oct 26 14:21:13 BST 2011: Transfer rate was 3212.5 MB/s

在服务器上和客户端打印

Wed Oct 26 14:21:03 BST 2011 Sending for 10.0 seconds.
Wed Oct 26 14:21:13 BST 2011 ... sent.
Wed Oct 26 14:21:13 BST 2011 ... received 33691287552
Send and received 3212.8 MB/s

注意:由于将客户端发送到服务器的所有内容都被服务器发送到客户端,因此传输的总金额会翻倍。


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class EchoServerMain {
  public static void main(String... args) throws IOException {
    int port = args.length < 1 ? 55555 : Integer.parseInt(args[0]);
    ServerSocketChannel ss = ServerSocketChannel.open();
    ss.socket().bind(new InetSocketAddress(port));
    while (!ss.socket().isClosed()) {
      SocketChannel s = ss.accept();
      System.out.println(new Date() + ": Accepted a connection");
      long start = System.nanoTime();
      ByteBuffer bytes = ByteBuffer.allocateDirect(32*1024);
      int len;
      long total = 0;
      // Thank you @EJP, for a more elegant single loop.
      while ((len = s.read(bytes)) >= 0 || bytes.position() > 0) { 
        bytes.flip(); 
        s.write(bytes); 
        bytes.compact(); 
        total += len;
      }
      long time = System.nanoTime() - start;
      System.out.printf(new Date() + ": Transfer rate was %.1f MB/s%n", total * 1e9 / 1024 / 1024 / time);
    }
    ss.close();
  }
}

import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class EchoClientMain {
  public static void main(String ... args) throws IOException {
    String hostname = args.length < 1 ? "localhost" : args[0];
    int port = args.length < 2 ? 55555 : Integer.parseInt(args[1]);
    double seconds = args.length < 3 ? 10 : Double.parseDouble(args[2]);

    SocketChannel s = SocketChannel.open(new InetSocketAddress(hostname, port));
    s.configureBlocking(false);
    ByteBuffer bytes = ByteBuffer.allocateDirect(32*1024);

    System.out.printf(new Date()+ " Sending for %.1f seconds.%n", seconds);
    long start = System.nanoTime();
    long dataSent = 0, dataReceived = 0;
    // run for 10 seconds.
    while(start + seconds*1e9 > System.nanoTime()) {
      bytes.clear();
      int wlen = s.write(bytes);
      if (wlen < 0) throw new IOException();
      dataSent += wlen;

      bytes.clear();
      int rlen = s.read(bytes);
      if (rlen < 0) throw new EOFException();
      dataReceived += rlen;
    }
    System.out.println(new Date()+ " ... sent.");

    while(dataReceived < dataSent) {
      bytes.clear();
      int rlen = s.read(bytes);
      if (rlen < 0) throw new EOFException();
      dataReceived += rlen;
    }
    s.close();
    long time = System.nanoTime() - start;
    System.out.println(new Date()+ " ... received "+dataReceived);
    System.out.printf("Send and received %.1f MB/s%n", dataReceived * 1e9/1024/1024/time);
  }
}

答案 2 :(得分:0)

你的计时器错了!您应该在接受连接后启动它,而不是在启动主机时启动它

您是否尝试增加缓冲区大小?

答案 3 :(得分:0)

使用计算机之间的有线快速以太网(100Mbit / s)链接测试相同的程序(然后可能使用1Gbit链接)。这样,您就可以了解转移率是否实际上受到您的计划或链接的限制。

答案 4 :(得分:0)

只需设置一个非常大的套接字发送缓冲区,如果可能的话,在接收器上设置一个非常大的套接字接收缓冲区。代码'优化'对这些场景基本没有贡献。