NIO(NO-Blocking I/O)模型

NIO 的一些关键概念和组件:

Channels(通道):代表到实体(如文件、套接字等)的开放连接,可以通过通道读写数据。

Buffers(缓冲区):是数据容器,用于存储不同类型的数据。通道读取数据到缓冲区,或者从缓冲区写入数据到通道。

Selectors(选择器):如果应用需要处理多个通道,选择器可以监控多个通道的 I/O 事件,如连接请求、数据到达等。这使得单个线程能够管理多个通道,从而提高效率。

⼯作原理可以分为以下⼏个步骤:

通过 FileChannel 、 SocketChannel 、 ServerSocketChannel 等类的静态⽅法打开 Channel;

open() 打开⼀个通道,创建⼀个或多个 Buffer ,⽤于读取或写⼊数据;

将数据写⼊ Buffer ,然后将 Buffer 中的数据写⼊ Channel ;

从 Channel 读取数据: 将 Channel 中的数据读取到 Buffer 中;

注册 Channel 到 Selector: 通过 Selector 监听⼀个或多个 Channel ,当 Channel 上发⽣感兴趣的事件时, Selector 将通知程序;

处理事件: 在⼀个循环中调⽤ Selector 的 select() ⽅法,该⽅法会阻塞直到⾄少⼀个注册的 Channel发⽣了感兴趣的事件。然后通过迭代 selectedKeys() 获取SelectionKey ,从⽽得知哪个 Channel 上发⽣了事件;

java 复制代码
//简单使用
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.ByteBuffer;

public class NioExample {
    public static void main(String[] args) {
        String filePath = "/path/to/file.txt";
        
        try {
            // 创建一个通道
            java.nio.channels.SeekableByteChannel channel = Files.newByteChannel(Paths.get(filePath));
            
            // 创建一个缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024); // 1KB buffer
            
            // 从通道读取数据到缓冲区
            while (channel.read(buffer) > 0) {
                // 切换为读模式
                buffer.flip();
                
                // 读取数据
                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }
                
                // 清空缓冲区,准备下一次读取
                buffer.clear();
            }
            
            // 关闭通道
            channel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
java 复制代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class SimpleNioServer {
    public void startServer(int port) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select(); // Wait for an event
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();

            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                
                if (key.isAcceptable()) {
                    registerClient(serverSocketChannel, selector);
                }
                
                if (key.isReadable()) {
                    answerClient(key);
                }

                iter.remove();
            }
        }
    }

    private void registerClient(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        SocketChannel clientChannel = serverSocketChannel.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("New client connected: " + clientChannel);
    }

    private void answerClient(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(256);
        int read = clientChannel.read(buffer);

        if (read == -1) {
            clientChannel.close();
            System.out.println("Client disconnected: " + clientChannel);
            return;
        }

        buffer.flip();
        while (buffer.hasRemaining()) {
            clientChannel.write(buffer);
        }
        buffer.clear();
    }

    public static void main(String[] args) {
        SimpleNioServer server = new SimpleNioServer();
        try {
            server.startServer(8080);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,startServer 方法会启动服务器,监听指定端口上的连接请求。当 selector.select() 调用返回时,意味着至少有一个通道准备好进行操作。如果事件是 OP_ACCEPT(即有新的连接请求),registerClient 方法会被调用以接受连接并注册新的客户端通道以读取数据。如果事件是 OP_READ(即有数据可读),answerClient 方法会被调用以读取数据,并将数据回显给客户端。

相关推荐
Swift社区1 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT2 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy2 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss4 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续4 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0444 小时前
ReAct模式解读
java·ai
轮到我狗叫了5 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
Volunteer Technology6 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量
栗子~~6 小时前
bat脚本- 将jar 包批量安装到 Maven 本地仓库
java·maven·jar