Java NIO 学习笔记(二)----汇集和分散,通道到通道

目录:
Java NIO 学习笔记(一)----概述,Channel/Buffer
Java NIO 学习笔记(二)----汇集和分散,通道到通道
Java NIO 学习笔记(三)----Selector
Java NIO 学习笔记(四)----文件通道和网络通道
Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
Java NIO 学习笔记(七)----NIO/IO 的对比和总结html

Scatter / Gather 通道的汇集和分散操做

NIO 具备内置的 scatter/gather 支持,用于描述读取和写入通道的操做。java

  • 分散(scatter)地从 Channel 中读取是将数据读入多个 Buffer 的操做。 所以,通道未来自通道的数据“分散”到多个缓冲区中。
  • 汇集(gather)地写入 Channel 是未来自多个缓冲区的数据写入单个通道的操做。 所以,通道未来自多个缓冲区的数据“收集”到同一个通道中。

通道的汇集和分散操做在须要将传输的数据分开处理的场合很是有用,例如,若是消息由标题和正文组成,则能够将标题和正文保留在单独的缓冲区中,这样作能够更容易处理标题和正文。数组

Scattering Reads 分散读取

是指将数据从单个通道读入多个缓冲区:网络

image

下面是一个代码示例,演示如何执行分散读取:dom

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 汇集写入

“汇集写入”未来自多个缓冲区的数据写入单个通道:code

image

一个示例:htm

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。

相关文章
相关标签/搜索