第6章 分散和聚集
6.1 概述
分散/聚集 I/O 是使用多個而不是單個緩衝區來保存數據的讀寫方法。
一個分散的讀取就像一個常規通道讀取,只不過它是將數據讀到一個緩衝區數組中而不是讀到單個緩衝區中。同樣地,一個聚集寫入是向緩衝區數組而不是向單個緩衝區寫入數據。
分散/聚集 I/O 對於將數據流劃分爲單獨的部分很有用,這有助於實現複雜的數據格式。
6.2 分散/聚集 I/O
通道可以有選擇地實現兩個新的接口: ScatteringByteChannel 和 GatheringByteChannel。一個 ScatteringByteChannel 是一個具有兩個附加讀方法的通道:
long read( ByteBuffer[] dsts ); long read( ByteBuffer[] dsts, int offset, int length );
這些 long read()方法很像標準的 read 方法,只不過它們不是取單個緩衝區而是取一個緩衝區數組。
在分散讀取中,通道依次填充每個緩衝區。填滿一個緩衝區後,它就開始填充下一個。在某種意義上,緩衝區數組就像一個大緩衝區。
6.3 分散/聚集的應用
分散/聚集 I/O 對於將數據劃分爲幾個部分很有用。例如,您可能在編寫一個使用消息對象的網絡應用程序,每一個消息被劃分爲固定長度的頭部和固定長度的正文。您可以創建一個剛好可以容納頭部的緩衝區和另一個剛好可以容難正文的緩衝區。當您將它們放入一個數組中並使用分散讀取來向它們讀入消息時,頭部和正文將整齊地劃分到這兩個緩衝區中。
我們從緩衝區所得到的方便性對於緩衝區數組同樣有效。因爲每一個緩衝區都跟蹤自己還可以接受多少數據,所以分散讀取會自動找到有空間接受數據的第一個緩衝區。在這個緩衝區填滿後,它就會移動到下一個緩衝區。
6.4 聚集寫入
聚集寫入類似於分散讀取,只不過是用來寫入。它也有接受緩衝區數組的方法:
long write( ByteBuffer[] srcs ); long write( ByteBuffer[] srcs, int offset, int length );
聚集寫對於把一組單獨的緩衝區中組成單個數據流很有用。爲了與上面的消息例子保持一致,您可以使用聚集寫入來自動將網絡消息的各個部分組裝爲單個數據流,以便跨越網絡傳輸消息。
從例子程序UseScatterGather.java 中可以看到分散讀取和聚集寫入的實際應用。
// UseScatterGather import java.io.*; import java.net.*; import java.nio.*; import java.nio.channels.*; public class UseScatterGather { static private final int firstHeaderLength = 2; static private final int secondHeaderLength = 4; static private final int bodyLength = 6; static public void main( String args[] ) throws Exception { if (args.length!=1) { System.err.println( "Usage: java UseScatterGather port" ); System.exit( 1 ); } int port = Integer.parseInt( args[0] ); ServerSocketChannel ssc = ServerSocketChannel.open(); InetSocketAddress address = new InetSocketAddress( port ); ssc.socket().bind( address ); int messageLength = firstHeaderLength + secondHeaderLength + bodyLength; ByteBuffer buffers[] = new ByteBuffer[3]; buffers[0] = ByteBuffer.allocate( firstHeaderLength ); buffers[1] = ByteBuffer.allocate( secondHeaderLength ); buffers[2] = ByteBuffer.allocate( bodyLength ); SocketChannel sc = ssc.accept(); while (true) { // Scatter-read into buffers int bytesRead = 0; while (bytesRead < messageLength) { long r = sc.read( buffers ); bytesRead += r; System.out.println( "r "+r ); for (int i=0; i<buffers.length; ++i) { ByteBuffer bb = buffers[i]; System.out.println( "b "+i+" "+bb.position()+" "+bb.limit() ); } } // Process message here // Flip buffers for (int i=0; i<buffers.length; ++i) { ByteBuffer bb = buffers[i]; bb.flip(); } // Scatter-write back out long bytesWritten = 0; while (bytesWritten<messageLength) { long r = sc.write( buffers ); bytesWritten += r; } // Clear buffers for (int i=0; i<buffers.length; ++i) { ByteBuffer bb = buffers[i]; bb.clear(); } System.out.println( bytesRead+" "+bytesWritten+" "+messageLength ); } } }