簡體   English   中英

Java NIO:transferFrom直到流結束

[英]Java NIO: transferFrom until end of stream

我正在玩NIO庫。 我正在嘗試在端口8888上偵聽連接,一旦接受連接,就將所有內容從該通道轉儲到somefile

我知道如何使用ByteBuffers ,但我想讓它與所謂的超高效FileChannel.transferFrom

這就是我得到的:

ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.socket().bind(new InetSocketAddress(8888));

SocketChannel sChannel = ssChannel.accept();
FileChannel out = new FileOutputStream("somefile").getChannel();

while (... sChannel has not reached the end of the stream ...)     <-- what to put here?
    out.transferFrom(sChannel, out.position(), BUF_SIZE);

out.close();

所以,我的問題是:我如何表達“從一些頻道transferFrom直到達到流尾”


編輯:將1024更改為BUF_SIZE,因為所使用的緩沖區大小與問題無關。

處理案件的方法很少。 一些背景信息如何在內部實現trasnferTo / From以及何時可以實現。

  • 首先,您應該知道xfer需要多少字節,即使用FileChannel.size()來確定可用的最大值並對結果求和。 案例指的是FileChannel.trasnferTo(socketChanel)
  • 該方法不返回-1
  • 該方法在Windows上進行模擬。 Windows沒有從filedescriptor到socket xfer的API函數,它確實從name指定的文件中有一(2)到xfer - 但這與java API不兼容。
  • 在Linux上,使用標准的sendfile (或sendfile64),在Solaris上稱為sendfilev64

簡而言之for (long xferBytes=0; startPos + xferBytes<fchannel.size();) doXfer()將用於從文件 - >套接字傳輸。 沒有OS函數從socket轉移到文件(OP感興趣)。 由於套接字數據不是操作系統緩存,因此無法有效地完成,它是模擬的。 實現副本的最佳方法是使用帶有套接字讀緩沖區的輪詢直接ByteBuffer標准循環。 因為我只使用涉及選擇器的非阻塞IO。

話雖這么說: 我想讓它與所謂的超高效“? - 它效率不高並且它在所有操作系統上模擬,因此當套接字正常關閉時它將最終轉移。該功能將如果有任何轉移(如果套接字是可讀和打開的),甚至不拋出繼承的IOException。

我希望答案很清楚:當源是文件時,會發生File.transferFrom唯一有趣的用法。 最有效(也是最有趣的情況)是file-> socket和file-> file是通過filechanel.map / unmap(!!)實現的

我不確定,但JavaDoc說:

嘗試從源通道讀取計數字節,並將它們從給定位置開始寫入該通道的文件。 調用此方法可能會也可能不會傳輸所有請求的字節; 是否這樣做取決於渠道的性質和狀態 如果源通道剩余的字節數少於計數字節,或者源通道非阻塞且輸入緩沖區中可立即使用的字節數少於計數字節,則將傳輸少於請求的字節數。

我想你可以說告訴它復制無限字節(當然不是在循環中)將完成這項工作:

out.transferFrom(sChannel, out.position(), Integer.MAX_VALUE);

所以,我想當套接字連接關閉時,狀態將會改變,這將停止transferFrom方法。

但正如我已經說過的那樣:我不確定。

直接回答你的問題:

while( (count = socketChannel.read(this.readBuffer) )  >= 0) {
   /// do something
}

但是,如果這是你做的,你不會使用非阻塞IO的任何好處,因為你實際上使用它完全阻止IO。 非阻塞IO的意思是1個網絡線程可以同時為多個客戶端服務:如果沒有任何東西可以從一個通道讀取(即count == 0 ),您可以切換到其他通道(屬於其他客戶端連接)。

因此,循環應該實際迭代不同的通道,而不是從一個通道讀取,直到它結束。

看看這個教程: http//rox-xmlrpc.sourceforge.net/niotut/我相信它會幫助你理解這個問題。

據稱是超高效的FileChannel.transferFrom。

如果你想要DMA訪問和非阻塞IO的好處,最好的方法是對文件進行內存映射,然后只需從套接字讀入內存映射緩沖區。

但這需要您預先分配文件。

這條路:

URLConnection connection = new URL("target").openConnection();
File file = new File(connection.getURL().getPath().substring(1));
FileChannel download = new FileOutputStream(file).getChannel();

while(download.transferFrom(Channels.newChannel(connection.getInputStream()),
        file.length(), 1024) > 0) {
    //Some calculs to get current speed ;)
}

transferFrom()返回一個計數。 只需繼續調用它,推進位置/偏移,直到它返回零。 但是從大於1024的計數開始,更像是一兆字節或兩兆,否則你不會從這種方法中獲得太多好處。

編輯為了解決下面的所有評論,文檔說“如果源通道剩余的字節數少於計數字節,或者如果源通道是非阻塞且立即數少於字節數,則將傳輸少於請求的字節數在其輸入緩沖區中可用。“ 因此,如果您處於阻塞模式,它將不會返回零,直到源中沒有任何內容。 因此循環直到它返回零是有效的。

編輯2

轉移方法肯定是錯誤設計的。 應該將它們設計為在流結束時返回-1,就像所有read()方法一樣。

建立在其他人寫的基礎上,這是一個簡單的幫助方法,它實現了目標:

public static void transferFully(FileChannel fileChannel, ReadableByteChannel sourceChannel, long totalSize) {
    for (long bytesWritten = 0; bytesWritten < totalSize;) {
        bytesWritten += fileChannel.transferFrom(sourceChannel, bytesWritten, totalSize - bytesWritten);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM