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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://coderlzw.gitbook.io/backend/io-liu/nio/channel-he-buffer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
