使文件传输更高效Java

我有两台无线电脑连接到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服务器。

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

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

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

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

我建议你尝试下面打印的代码

 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); } } 

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

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

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

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