当前位置: 首页 > article >正文

问:JAVA NIO模型中selector/buffer/channel如何协作?

在Java NIO(New Input/Output)中,SelectorChannelBuffer是三个核心概念,它们共同构成了NIO的高效非阻塞I/O模型。

示例:非阻塞服务

1. 概念解释
  • Channel:Channel是NIO中的基本I/O操作抽象。它表示到实体(如一个硬件设备、一个文件、一个网络套接字或能进行一种或多种不同的I/O操作的程序组件)的开放连接,如读操作和写操作。

  • Buffer:Buffer是一个用于特定数据类型的容器。它是NIO数据传输的基础,所有的I/O操作都是通过缓冲区进行的。缓冲区实质上是一个数组,通常是一个字节数组,但它不仅仅是一个数组,缓冲区还提供了对数据的结构化访问,并可以跟踪系统的读/写进程。

  • Selector:Selector允许单个线程处理多个Channel。如果你的应用程序需要同时处理多个Channel(例如,处理多个网络连接),使用Selector会是一个很好的选择。Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的连接、读或者写事件,这个Channel就被认为是就绪的,Selector会返回这些就绪的Channel。

2. 示例代码

以下是一个简单的非阻塞服务器示例,它使用SelectorServerSocketChannelByteBuffer来接收客户端连接并读取数据。

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 {

    public static void main(String[] args) throws IOException {
        // 打开一个Selector
        Selector selector = Selector.open();

        // 打开一个ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false); // 设置为非阻塞模式
        serverChannel.socket().bind(new InetSocketAddress(8080)); // 绑定端口

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

        // 创建一个缓冲区,用于读取数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while (true) {
            // 选择器等待就绪的Channel
            selector.select();

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

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                keyIterator.remove(); // 从集合中移除当前key,防止重复处理

                // 检查key的状态
                if (key.isAcceptable()) {
                    // 接受新的连接
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = server.accept();
                    clientChannel.configureBlocking(false); // 设置为非阻塞模式

                    // 注册新的SocketChannel到Selector上,监听读事件
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("Accepted new connection from client: " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取数据
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    buffer.clear(); // 清空缓冲区
                    int bytesRead = clientChannel.read(buffer); // 读取数据到缓冲区

                    if (bytesRead > 0) {
                        buffer.flip(); // 切换为读模式
                        while (buffer.hasRemaining()) {
                            System.out.print((char) buffer.get()); // 打印读取的数据
                        }
                        buffer.clear(); // 准备下一次读取
                    } else if (bytesRead == -1) {
                        // 客户端关闭连接
                        clientChannel.close();
                    }
                }
            }
        }
    }
}
3. 解释
  • 打开SelectorSelector.open()方法用于打开一个新的Selector实例。

  • 打开ServerSocketChannelServerSocketChannel.open()方法用于打开一个新的ServerSocketChannel实例,它表示服务器端的套接字。

  • 配置非阻塞模式configureBlocking(false)方法将Channel设置为非阻塞模式。在非阻塞模式下,I/O操作会立即返回,而不会等待操作完成。

  • 绑定端口socket().bind(new InetSocketAddress(8080))方法将ServerSocketChannel绑定到指定的端口(8080)。

  • 注册Selectorregister(selector, SelectionKey.OP_ACCEPT)方法将ServerSocketChannel注册到Selector上,并指定它感兴趣的事件类型(在这里是接受连接事件)。

  • 轮询Selectorselector.select()方法会阻塞当前线程,直到至少有一个通道在你注册的事件上就绪了。

  • 处理就绪的Channel:通过遍历selector.selectedKeys()集合,获取就绪的SelectionKey,并根据key的状态进行相应的处理(如接受连接、读取数据等)。

  • 使用Buffer读取数据clientChannel.read(buffer)方法将数据从Channel读取到Buffer中。buffer.flip()方法用于切换Buffer的读/写模式。buffer.get()方法用于从Buffer中读取数据。

通过上述解析,我们可以看到SelectorChannelBuffer是如何协同工作的,以实现一个高效的非阻塞服务器。Selector用于监听多个Channel的状态,Channel负责实际的I/O操作,而Buffer则作为数据传输的容器。这种模型使得单个线程能够处理多个网络连接,从而大大提高了程序的并发性能和I/O操作效率。


http://www.kler.cn/a/300431.html

相关文章:

  • ent.SetDatabaseDefaults()
  • 郑州大学2022级大三期末复习总结(数据库,传感器,嵌入式,人工智能,移动终端开发,计算机英语)
  • ARM学习(42)CortexM3/M4 MPU配置
  • 深度学习 DAY1:RNN 神经网络及其变体网络(LSTM、GRU)
  • 新星杯-ESP32智能硬件开发--ESP32的I/O组成-系统中断矩阵
  • 《Vue3 九》动画
  • C语言 ——— 条件编译指令实际用途
  • SQL数据分析形成笛卡尔积数据重复计算问题
  • 金融壹账通:智能面审解决方案“大显身手”
  • 故障恢复(残次版)
  • 层归一化(201607)
  • Centos7.9下的snmp搭建及实例
  • Day7 | Java框架 | SpringMVC
  • 用Python-pptx定制你的形状效果
  • 泰语快速学习方法!速成方法学习!
  • Java:继承和多态(1)
  • 在 Ubuntu 下通过 Docker 部署 Misskey 服务器
  • 视频推拉流/直播点播EasyDSS平台安装失败并报错“install mediaserver error”是什么原因?
  • C#使用TCP-S7协议读写西门子PLC(二)
  • C#中的类
  • 力扣sql五十题——连接
  • 银河麒麟国产化系统(或者是Linux)一键安装docker和docker-compose
  • C++ 封装 DLL 供 Unity 调用
  • 开源Devops工具-Ansible
  • 浅谈C#之任务调度TaskScheduler
  • Java | Leetcode Java题解之第398题随机数索引