NIO学习笔记

NIO学习笔记

NIO是什么?

NIO(Non-blocking I/O)是Java中的一种高性能I/O模型,用于处理大量并发连接。与传统的阻塞式I/O模型不同,NIO允许在单个线程上管理多个通道(网络连接或文件IO),并使用选择器(Selector)实现非阻塞式的事件驱动IO操作。这种方式可以大大减少线程的数量,提高系统的并发能力和性能。

哪些场景需要 NIO

Java NIO 适用于需要高性能、高并发的网络应用程序场景。以下是一些适合使用Java NIO的场景:

  1. 高并发的网络服务器:NIO允许一个线程管理多个连接,这使得它非常适合处理高并发的网络通信,比如Web服务器、聊天服务器等。

  2. 实时通信应用:对于需要实时处理消息、低延迟的应用程序,NIO提供了非阻塞IO和事件驱动的特性,能够更好地满足这类需求。

  3. 大规模文件传输:如果需要传输大量的数据,特别是大文件,NIO的非阻塞IO特性可以使得系统更高效地利用资源。

  4. 多协议支持:NIO可以轻松地支持多种协议,如TCP、UDP等,因为它提供了更灵活的IO操作方式。

  5. 游戏服务器:游戏服务器通常需要处理大量的并发连接和实时通信,NIO能够提供良好的性能和可扩展性,满足游戏服务器的需求。

总的来说,如果应用程序需要处理大量的并发连接、需要实时通信或者需要高性能的网络IO操作,那么就适合使用Java NIO。

代码示例

下面是一个简单的Java NIO实现的聊天弹幕服务器和客户端的代码示例:

// 服务器端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class ChatServer {
    private static final int PORT = 8080;

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
        serverSocketChannel.configureBlocking(false);

        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Server started on port " + PORT);

        while (true) {
            selector.select();

            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    acceptConnection(selector, serverSocketChannel);
                } else if (key.isReadable()) {
                    readMessage(key);
                }
            }
        }
    }

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

    private static void readMessage(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = channel.read(buffer);

        if (bytesRead == -1) {
            channel.close();
            key.cancel();
            return;
        }

        buffer.flip();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        String message = new String(bytes).trim();
        System.out.println("Received message: " + message);

        broadcastMessage(channel, message);
    }

    private static void broadcastMessage(SocketChannel sender, String message) throws IOException {
        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());

        for (SelectionKey key : sender.selector().keys()) {
            if (key.isValid() && key.channel() instanceof SocketChannel && key.channel() != sender) {
                SocketChannel channel = (SocketChannel) key.channel();
                channel.write(buffer);
                buffer.rewind();
            }
        }
    }
}
// 客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class ChatClient {
    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 8080;

    public static void main(String[] args) {
        try {
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(SERVER_HOST, SERVER_PORT));

            Thread receiverThread = new Thread(() -> {
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                try {
                    while (true) {
                        buffer.clear();
                        int bytesRead = socketChannel.read(buffer);
                        if (bytesRead == -1) {
                            break;
                        }
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String message = new String(bytes).trim();
                        System.out.println("Received message: " + message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            receiverThread.start();

            Scanner scanner = new Scanner(System.in);
            while (true) {
                String message = scanner.nextLine();
                ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
                socketChannel.write(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这个简单的示例实现了一个基于Java NIO的简易聊天弹幕系统,其中服务器接受来自客户端的连接,并将客户端发送的消息广播给所有连接的客户端。客户端可以发送消息到服务器,并接收其他客户端发送的消息。

NIO如何实现允许在单个线程上管理多个通道?

Java NIO 实现允许在单个线程上管理多个通道的核心是通过 Selector(选择器)机制。Selector 是一个多路复用器,用于监听多个通道的事件,并且当事件发生时,能够通知应用程序进行处理。Selector 提供了一种高效的方式来处理多个通道的 I/O 事件,避免了传统的阻塞 I/O 模型中需要为每个连接创建一个线程的开销。

Selector 的工作原理如下:

  1. 将多个 Channel 注册到 Selector 上,并指定感兴趣的事件,如读、写、连接、接收等。
  2. Selector 负责监听这些通道上发生的事件,一旦某个通道上的事件发生,Selector 就会返回对应的 SelectionKey。
  3. 应用程序可以遍历 SelectionKey,获取发生事件的通道,然后进行相应的处理。

这种机制使得一个线程可以同时管理多个通道的 I/O 操作,大大减少了线程的数量,提高了系统的并发性能。

Java NIO 实现允许在单个线程上管理多个通道的核心是通过 Selector(选择器)机制。Selector 是一个多路复用器,用于监听多个通道的事件,并且当事件发生时,能够通知应用程序进行处理。Selector 提供了一种高效的方式来处理多个通道的 I/O 事件,避免了传统的阻塞 I/O 模型中需要为每个连接创建一个线程的开销。

Selector 的工作原理如下:

  1. 将多个 Channel 注册到 Selector 上,并指定感兴趣的事件,如读、写、连接、接收等。

  2. Selector 负责监听这些通道上发生的事件,一旦某个通道上的事件发生,Selector 就会返回对应的 SelectionKey。

  3. 应用程序可以遍历 SelectionKey,获取发生事件的通道,然后进行相应的处理。

这种机制使得一个线程可以同时管理多个通道的 I/O 操作,大大减少了线程的数量,提高了系统的并发性能。

以下是一个简单的示例代码,演示了如何使用 Selector 实现单个线程管理多个通道:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioServer {
    private static final int PORT = 8080;

    public static void main(String[] args) throws IOException {
        // 创建 Selector
        Selector selector = Selector.open();

        // 创建 ServerSocketChannel 并绑定端口
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
        serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式
        // 将 ServerSocketChannel 注册到 Selector,并指定关注的事件为接收连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Server started on port " + PORT);

        while (true) {
            // Selector 调用 select() 方法进行监听,阻塞直到有事件发生
            selector.select();

            // 获取就绪的事件集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) { // 如果是接收连接事件
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = serverChannel.accept(); // 接收客户端连接
                    clientChannel.configureBlocking(false); // 设置为非阻塞模式
                    clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件
                    System.out.println("Accepted new connection from " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) { // 如果是可读事件
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int bytesRead = clientChannel.read(buffer); // 读取数据到缓冲区
                    if (bytesRead != -1) {
                        buffer.flip(); // 切换为读模式
                        byte[] data = new byte[buffer.remaining()];
                        buffer.get(data);
                        System.out.println("Received message: " + new String(data));
                    } else {
                        clientChannel.close(); // 关闭连接
                    }
                }
            }
        }
    }
}

相关推荐

  1. NIO学习笔记

    2024-03-17 13:14:03       19 阅读
  2. 算法学习笔记Nim游戏)

    2024-03-17 13:14:03       10 阅读
  3. <span style='color:red;'>NIO</span><span style='color:red;'>学习</span>

    NIO学习

    2024-03-17 13:14:03      39 阅读
  4. mysql学习笔记NO.2

    2024-03-17 13:14:03       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 13:14:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 13:14:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 13:14:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 13:14:03       18 阅读

热门阅读

  1. dp动态规划的基本

    2024-03-17 13:14:03       20 阅读
  2. CCF CSP试题编号: 202312-2试题名称: 因子化简

    2024-03-17 13:14:03       17 阅读
  3. MongoDB聚合运算符:$eq

    2024-03-17 13:14:03       20 阅读
  4. 英语随笔,发散了 3.17

    2024-03-17 13:14:03       17 阅读
  5. web安全——sql注入漏洞知识点总结

    2024-03-17 13:14:03       17 阅读
  6. 嵌入式摄像头,获取视频要通过进程通讯?

    2024-03-17 13:14:03       20 阅读
  7. 外观模式实战运用

    2024-03-17 13:14:03       18 阅读
  8. Android中的设计模式---单例模式

    2024-03-17 13:14:03       19 阅读
  9. 大型语言模型与Scikit-learn:Scikit-LLM全面指南

    2024-03-17 13:14:03       18 阅读