2016-11-22 41 views
0

这将是通过套接字复制文件的最快方法吗?我尝试了几种方法,但我不确定我找到了有关传输和CPU使用情况的最快方法。 (最好成绩:175mBit /秒(SSD /千兆比特网络))通过套接字复制文件的最快方法

服务器:

ByteBuffer bb = ByteBuffer.allocate(packet_size); 
DataOutputStream data_out = new DataOutputStream(socket.getOutputStream()); 

while(working){ 

    int count =0; 
    int packet_size = in.readInt(); 
    long pos = in.readLong(); 
    if(filechannel.position()!=requested_pos){ 
     filechannel.position(requested_pos); 
    } 
    bb.limit(packet_size); 
    bb.position(0); 
    if((count=filechannel.read(bb))>0){ //FileInputStream.getChannel() 
     data_out.writeInt(count); 
     data_out.write(bb.array(),0,count); 
    }else{ 
    working=false; 
    } 
} 

客户:

for(long i=0;i<=steps;i++){ 

    data_out.writeInt(packet_size);  //requested packet size 
    data_out.writeLong(i*packet_size); //requested file position 

    count=in.readInt(); 
    bb.clear(); 
    bb.limit(count); 

    lastRead=0; 
    while(lastRead<count){ 
     lastRead+=in.read(bytes,lastRead,count-lastRead); 
    } 
    bb.put(bytes,0,count); 
    bb.position(0); 
    filechannel.write(bb); // filechannel over RandomAccessFile 
} 

有什么建议?

+2

您可以压缩块并在另一端解压缩。例如尝试一下snappy java – qwr

回答

0

您想使用NIO。

import java.nio.ByteBuffer; 
    import java.nio.channels.Channels; 
    import java.nio.channels.ReadableByteChannel; 
    import java.nio.channels.WritableByteChannel; 


public class FileServlet extends HttpServlet { 

     @Override 
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 

      try(final InputStream is = new BufferedInputStream((InputStream) <YOUR INPUT STREAM TO A FILE HERE>); 
       final OutputStream os = new BufferedOutputStream(response.getOutputStream());) { 

       fastCopy(is, os); 
      } 

} 

public static void fastCopy(final InputStream src, final OutputStream dest) throws IOException { 
    fastCopy(Channels.newChannel(src), Channels.newChannel(dest)); 
} 



    public static void fastCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException { 
     final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); 

     while(src.read(buffer) != -1) { 
      buffer.flip(); 
      dest.write(buffer); 
      buffer.compact(); 
     } 

     buffer.flip(); 

     while(buffer.hasRemaining()) { 
      dest.write(buffer); 
     } 
    }  
} 

} 
+1

Java NIO比Java IO更快是个谬论。 – fireandfuel

3

您只看到问题的一半。用于发送/接收的代码只是一个因素。无论你如何优化它,如果你使用不合适的参数设置你的套接字,性能会受到很大影响。

对于大数据传输,请确保套接字具有相当大的缓冲区。我会选择至少64kb,可能更大。发送和接收缓冲区可以独立设置,因为发送者需要一个大的(r)发送缓冲区,而接收者需要一个大的(r)接收缓冲区。

socket.setReceiveBufferSize(int); 
    socket.setSendBufferSize(int); 
    socket.setTcpNoDelay(false); 

设置TCP无延迟为OFF,除非你知道你在做什么,并确认后,你真的绝对需要它。它会从永远不会提高吞吐量,相反,它可能会牺牲吞吐量有利于减少延迟。

接下来的事情是定制您的发件人代码,以尽量保持缓冲区全部为。为了从文件读取最大速度并且写入套接字应该被分成两个独立的线程,使用某种队列相互通信。队列中的块应该相当大(至少几kb)。

同样,接收代码应尽最大努力保持接收缓冲区尽可能为空。同样,为了获得最大速度,这需要两个线程,一个读取套接字,另一个处理数据。在发件人之间排队等候。

队列的工作是从实际的网络传输中解读从文件读取数据到文件中的停顿,反之亦然。

以上是通用模式,无论传输通道如何,您都可以获得最大吞吐量。较慢的通道将保持完全饱和,无论是文件读取/写入还是网络传输。

可以调整缓冲区大小以挤出最后几个百分比的可能性能(我将以64kb开始,并且最大队列大小为1mb的队列中的8kb块开始,这应该提供性能相当接近最大可能)。

您可能遇到的另一个限制因素是TCP传输窗口缩放(特别是在高带宽,高延迟连接上)。除了确保接收器尽可能快地清空接收缓冲区之外,在Java方面你不能做任何事情。调整选项存在于操作系统级别。

+0

很好的答案。据推测缓冲区不需要在任何时候都“充满” - 它只需要保持充足状态,这样就不会有少于一个数据包。 – slim

+0

@slim是的,这正是我想传达的。 – Durandal

+0

谢谢,很好的答案! – SalkinD