Java NIO 學習筆記(二)----聚集和分散,通道到通道

目錄:
Java NIO 學習筆記(一)----概述,Channel/Buffer
Java NIO 學習筆記(二)----聚集和分散,通道到通道
Java NIO 學習筆記(三)----Selector

Scatter / Gather 通道的聚集和分散操作

NIO 具有內置的 scatter/gather 支持,用於描述讀取和寫入通道的操作。

  • 分散(scatter)地從 Channel 中讀取是將數據讀入多個 Buffer 的操作。 因此,通道將來自通道的數據“分散”到多個緩衝區中。
  • 聚集(gather)地寫入 Channel 是將來自多個緩衝區的數據寫入單個通道的操作。 因此,通道將來自多個緩衝區的數據“收集”到同一個通道中。

通道的聚集和分散操作在需要將傳輸的數據分開處理的場合非常有用,例如,如果消息由標題和正文組成,則可以將標題和正文保留在單獨的緩衝區中,這樣做可以更容易處理標題和正文。

Scattering Reads 分散讀取

是指將數據從單個通道讀入多個緩衝區:

image

下面是一個代碼示例,演示如何執行分散讀取:

public class ScatteringReads {
    public static void main(String[] args) throws IOException {

        ByteBuffer buffer1 = ByteBuffer.allocate(5); // 分配第一個緩衝區,大小爲 5
        ByteBuffer buffer2 = ByteBuffer.allocate(128);
        ByteBuffer[] buffers = {buffer1, buffer2}; // 兩個緩衝區的數組

        File file = new File("D:\\test\\1.txt"); // 文件內容是 012345678
        RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
        FileChannel channel = accessFile.getChannel();

        long data = channel.read(buffers); // 一次性把通道的數據讀入2個緩衝區
        System.out.println("Read: " + data); // Read 9

        System.out.println("開始讀取第一個 buffer :");
        buffer1.flip(); // 將 buffer 從寫入模式切換爲讀取模式
        while (buffer1.hasRemaining()) {
            System.out.print((char) buffer1.get()); // 每次讀取1byte,輸出 01234
        }

        System.out.println("\n開始讀取第二個 buffer :");

        buffer2.flip();
        while (buffer2.hasRemaining()) {
            System.out.print((char) buffer2.get()); // 輸出 5678
        }
    }
}

將會輸出:

Read: 9
開始讀取第一個 buffer :
01234
開始讀取第二個 buffer :
5678

注意多個緩衝區首先插入到數組中,然後將數組作爲參數傳遞給 channel.read() 方法。 然後,read()方法按照緩衝區在數組中出現的順序從通道寫入數據。 一旦緩衝區已滿,通道就會繼續填充下一個緩衝區。
分散讀取在移動到下一個緩衝區之前,必須先填充慢前一個緩衝區,這意味着它不適合大小不固定的消息。

Gathering Writes 聚集寫入

“聚集寫入”將來自多個緩衝區的數據寫入單個通道:

image

一個示例:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
//write data into buffers...

channel.write(bufferArray);

將緩衝區數組傳遞給 write() 方法,該方法按照在數組的順序寫入緩衝區的內容到單個通道,注意僅寫入緩衝區的 position 和 limit 之間的數據。 因此,如果緩衝區的容量爲 128 字節,但只包含 58 字節的內容,則只有 58 字節從該緩衝區寫入通道。 因此,與 Scattering Reads 相比,Gathering Writes 可以適應大小不固定的數據,因爲它只把包含內容部分的緩衝區寫入到通道。

Channel to Channel 通道到通道傳輸

在 NIO 中,如果其中一個通道是 FileChannel ,可以直接將數據從一個通道傳輸到另一個通道。 FileChannel 類有一個 transferTo() 和 transferFrom() 方法。

transferFrom() 和 transferTo()

FileChannel 對象的 transferFrom() 方法將數據從源通道傳輸到 FileChannel。 這是一個簡單的例子:

public class TransfetExample {
    public static void main(String[] args) throws IOException {
        RandomAccessFile fromFile = new RandomAccessFile("D:\\test\\input.txt", "rw");
        FileChannel fromChannel = fromFile.getChannel();

        RandomAccessFile toFile = new RandomAccessFile("D:\\test\\receive.txt", "rw");
        FileChannel toChannel = toFile.getChannel();

        long position = 0;
        long count = fromChannel.size();

        toChannel.transferFrom(fromChannel, position, count);
    }
}

參數 position 和 count,告訴目標文件中開始寫入的位置以及最大傳輸的字節數(總數)。 如果源通道的字節數少於 count ,則傳輸實際字節數。

此外,一些 SocketChannel 實現可能現在只傳輸 SocketChannel 在其內部緩衝區中準備好的數據 - 即使 SocketChannel 可能稍後有更多可用數據。 因此,它可能不會將請求的整個數據(count)從 SocketChannel 傳輸到 FileChannel 。

transferTo() 方法的效果除了目標和參數位置不一致,其他部分同 transferFrom() 方法一樣,上面代碼如果換成執行 fromChannel.transferTo(position, count, toChannel); input.txt 的內容同樣會被立即複製到 receive.txt。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章