【Java】NIO 简单介绍

简介

  • 从 Java 1.4 版本开始引入的一个新的 I/O API,可以替代标准的 Java I/O。
  • 提供了与标准 I/O 不同的工作方式,核心是 通道(Channel)、缓冲区(Buffer) 和 选择器(Selector)。
  • 支持非阻塞 I/O 操作,非常适合处理大量并发连接,是构建高性能网络应用的基础。
  • 多路复用的机制,适合构建高性能服务器应用

核心概念

  • Channel:类似于流(Stream),但可以双向读写
  • Buffer:一个用于存储数据的容器(本质上是一个数组)
  • Selector:用于监听多个 Channel 的事件(如连接、读就绪、写就绪)
    • 一个线程可以管理多个 Channel,实现单线程处理多路复用,极大地提高了 I/O 效率

代码示例

java 复制代码
public class NIOClient {
    public static void main(String[] args) throws IOException {
        // 1. 连接到服务器
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 8080));
        socketChannel.configureBlocking(false); // 设置为非阻塞(可选,这里为了演示NIO风格)

        System.out.println("已连接到服务器 localhost:8080");
        System.out.println("请输入消息(输入 'quit' 退出):");

        // 用于读取用户输入
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String userInput;

        // 用于读取服务器响应
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while ((userInput = reader.readLine()) != null) {
            if ("quit".equalsIgnoreCase(userInput)) {
                break;
            }

            // 2. 发送消息到服务器
            byte[] messageBytes = userInput.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.wrap(messageBytes);
            socketChannel.write(writeBuffer);
            System.out.println("已发送: " + userInput);

            // 3. 读取服务器的响应
            buffer.clear(); // 清空缓冲区,准备接收数据
            int bytesRead = socketChannel.read(buffer);
            if (bytesRead > 0) {
                buffer.flip(); // 切换为读模式
                byte[] responseBytes = new byte[buffer.remaining()];
                buffer.get(responseBytes);
                String response = new String(responseBytes);
                System.out.println("服务器回复: " + response);
            }
        }

        // 4. 关闭连接
        socketChannel.close();
        System.out.println("客户端已关闭。");
    }
}
java 复制代码
public class NIOServer {
    public static void main(String[] args) throws IOException {
        // 1. 打开 Selector
        Selector selector = Selector.open();

        // 2. 打开 ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false); // 设置为非阻塞模式
        serverChannel.bind(new InetSocketAddress(8080));

        // 3. 将 ServerSocketChannel 注册到 Selector,监听 ACCEPT 事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("NIO 服务器启动,监听端口 8080...");

        while (true) {
            // 4. 阻塞等待,直到有事件发生
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            // 5. 获取就绪的 SelectionKey 集合
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                // 6. 处理不同的事件
                if (key.isAcceptable()) {
                    // 接受新的连接
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = server.accept();
                    clientChannel.configureBlocking(false); // 设置为非阻塞
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("新客户端连接: " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取客户端数据
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    int bytesRead = clientChannel.read(buffer);
                    if (bytesRead > 0) {
                        buffer.flip();
                        byte[] data = new byte[buffer.remaining()];
                        buffer.get(data);
                        String message = new String(data);
                        System.out.println("收到消息: " + message);
                        // 回复客户端
                        ByteBuffer responseBuffer = ByteBuffer.wrap(("Echo: " + message).getBytes());
                        clientChannel.write(responseBuffer);
                    } else if (bytesRead == -1) {
                        // 客户端关闭连接
                        System.out.println("客户端断开: " + clientChannel.getRemoteAddress());
                        clientChannel.close();
                    }
                }

                // 7. 移除已处理的 key
                keyIterator.remove();
            }
        }
    }
相关推荐
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
范什么特西1 天前
web练习
java·前端·javascript
阿捞21 天前
JVM排查工具单
java·jvm·python
mfxcyh1 天前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode1 天前
Spring 依赖注入方式全景解析
java·后端·spring
爱吃烤鸡翅的酸菜鱼1 天前
Java 事件发布-订阅机制全解析:从原生实现到主流中间件
java·中间件·wpf·事件·发布订阅
无限码力1 天前
华为OD技术面真题 - JAVA开发- spring框架 - 7
java·开发语言·华为od·华为od面试真题·华为odjava八股文·华为odjava开发题目·华为odjava开发高频题目
Lyyaoo.1 天前
【JAVA基础面经】JAVA中的异常
java·开发语言
一定要AK1 天前
JVM 全体系深度解析笔记
java·jvm·笔记