Channel和Buffer

通常,NIO中的所有IO都以Channel开头,数据可以从Channel读入Buffer,也可以从Buffer写入Channel

以下是NIO中Channel实现类的列表,这些通道包括UDP+TCP网络IO和文件IO:

  • FileChannel :文件通道

  • DatagramChannel :数据报通道

  • SocketChannel :套接字通道

  • ServerSocketChannel :服务器套接字通道

以下是 NIO 中的核心 Buffer 实现,其实就是 7 种基本类型:

  • ByteBuffer

  • CharBuffer

  • ShortBuffer

  • IntBuffer

  • LongBuffer

  • FloatBuffer

  • DoubleBuffer

Channel(通道)

NIO 中的通道类似于流,单丝两者之间存在一些区别:

  • 通道可以读取和写入。流通常是单向的(读或写)

  • 通道可以异步读取和写入。

  • 通道始终读取或写入缓冲区,它只面向缓冲区。

// 文件内容 123456789
RandomAccessFile accessFile = new RandomAccessFile("D:\\test.txt", "rw");
FileChannel fileChannel = accessFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
// 将数据从Channel读取到Buffer中,返回读取的字节数
int data = fileChannel.read(buffer);

Buffer(缓冲区)

使用Buffer读取和写入数据通常遵循以下四个步骤:

  1. 将数据写入到缓冲区。

  2. 调用buffer.flip()反转读写模式。

  3. 从缓冲区读取数据。

  4. 调用buffer.clear()或者buffer.compact()清除缓冲区内容。

RandomAccessFile accessFile = new RandomAccessFile("D:\\test.txt", "rw");
FileChannel fileChannel = accessFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
// 将数据从Channel读取到Buffer中,返回读取的字节数
int data = fileChannel.read(buffer);
while(data != -1) {
    System.out.println("Read " + data);
    // 将Buffer从写模式切换到读模式
    buffer.flip();
    while(buffer.hasRemaining()) {
        // 每次读取1byte,循环输出 123456789
        System.out.println((char) buffer.get());
    }
    // 清空Buffer,将Buffer从读模式切换到写模式
    buffer.clear();
    data = fileChannel.read(buffer);
}
accessFile.close();java

Channel 通道只负责传输数据、不直接操作数据。操作数据都是通过Buffer缓冲区进行的。通常,通道可以分为两大类:文件通道和套接字通道,

FileChannel:用于文件IO的通道,支持文件的读写等操作。FileChannel允许在文件的任意位置进行数据传输,支持文件锁定以及内存映射文件等高级功能。FileChannel无法设置为非阻塞模式,因为它只适用于阻塞式文件操作。 SocketChannel:用于TCP套接字的IO通信。SocketChannel支持非阻塞模式,可以与Selector一起使用,实现高效的网络通信。Socket Channel允许连接到远程主机,进行数据传输。 与之匹配的有ServerSocketChannel:用于坚挺TCP套接字连接的通道。与SocketChannel类似,ServerSocketChannel也支持非阻塞模式,并可以与Selector一起使用。 DatagramChannel:用于UDP套接字IO通道,支持非阻塞

FileChannel

FileChannel.open(Paths.get("docs/配套教程.md"), StandardOpenOption.WRITE);
  1. 使用FileChannel 配合 ByteBuffer 缓冲区实现文件复制的功能:

try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
    FileChannel destinationChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger1.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {

  ByteBuffer buffer = ByteBuffer.allocate(1024);

  while (sourceChannel.read(buffer) != -1) {
      buffer.flip();
      destinationChannel.write(buffer);
      buffer.clear();
  }
}
  1. 使用内存映射文件(MappedByteBuffer)的方式实现文件复制的功能(直接操作缓冲区):

try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
      FileChannel destinationChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger2.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ)) {

    long fileSize = sourceChannel.size();
    MappedByteBuffer sourceMappedBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
    MappedByteBuffer destinationMappedBuffer = destinationChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

    for (int i = 0; i < fileSize; i++) {
        byte b = sourceMappedBuffer.get(i);
        destinationMappedBuffer.put(i, b);
    }
}
  1. 通道之间通过transfer()实现数据的传输(直接操作缓冲区):

try (FileChannel sourceChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);
      FileChannel destinationChannel = FileChannel.open(Paths.get("logs/javabetter/itwanger3.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ)) {
    sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
} catch (IOException e) {
    throw new RuntimeException(e);
}

最后更新于

这有帮助吗?