2016-11-22 40 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