# 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 中的通道类似于流，单丝两者之间存在一些区别：

* 通道可以读取和写入。流通常是单向的（读或写）
* 通道可以异步读取和写入。
* 通道始终读取或写入缓冲区，它只面向缓冲区。

```java
// 文件内容 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()`清除缓冲区内容。

```java
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

```java
FileChannel.open(Paths.get("docs/配套教程.md"), StandardOpenOption.WRITE);
```

1. 使用FileChannel 配合 ByteBuffer 缓冲区实现文件复制的功能：

```java
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();
  }
}
```

2. 使用内存映射文件（MappedByteBuffer）的方式实现文件复制的功能(直接操作缓冲区)：

```java
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);
    }
}
```

3. 通道之间通过transfer()实现数据的传输(直接操作缓冲区)：

```java
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);
}
```
