NIO(Non-blocking I/O)处理机制

news/2024/10/18 7:43:56/

典型的 NIO 事件处理流程

在 Java NIO (Non-blocking I/O) 中,事件驱动模型使得应用程序能够高效地管理多个并发的 I/O 操作。通过 Selector,NIO 使得单个线程可以监听多个通道的事件(如连接请求、读写数据)。以下是对典型 NIO 事件处理流程的详细讲解,包括 Java 代码示例和 UML 时序图。

NIO 事件处理的基本步骤:

  1. 初始化 Selector

    • 创建一个 Selector,用于管理多个通道(Channel)和它们的事件。
  2. 注册通道

    • 通过 ServerSocketChannelSocketChannel 监听 I/O 事件,并将它们注册到 Selector 上,指定感兴趣的事件类型(如:OP_ACCEPT, OP_READ, OP_WRITE)。
  3. 轮询事件

    • Selector.select() 方法阻塞,直到至少有一个通道的事件已经准备好。
  4. 事件处理

    • 处理已准备好的事件(如连接请求、读取数据等)。每当事件发生时,Selector 会返回相关通道和事件的集合,应用程序根据事件类型进行处理。
  5. 数据读写

    • 对于读写事件,SocketChannel 会被用来进行数据的读取和写入。如果事件是连接请求(OP_ACCEPT),ServerSocketChannel 会接受连接并返回新的 SocketChannel
  6. 关闭通道或重新注册

    • 处理完事件后,如果通道不再需要,应该关闭它,释放相关资源;或者根据需求,将通道重新注册到 Selector 上以处理其他事件。

示例场景:TCP 服务端处理客户端连接

以下示例展示了一个简单的 TCP 服务端,使用 NIO 处理多个客户端的连接请求,并对数据进行读写。

关键类:

  • ServerSocketChannel:用于监听客户端的连接请求。
  • SocketChannel:与客户端建立连接后,用于进行数据读写。
  • Selector:管理多个通道的事件。

事件处理流程:

  1. 客户端连接请求
  2. ServerSocketChannel 注册 OP_ACCEPT 事件。
  3. Selector.select() 阻塞,等待事件。
  4. Selector 发现 OP_ACCEPT 事件,通知 ServerSocketChannel
  5. ServerSocketChannel 接受连接,并创建 SocketChannel
  6. SocketChannel 注册 OP_READ 事件。
  7. Selector.select() 阻塞,等待 OP_READ 事件。
  8. 客户端发送数据,Selector 检测到 OP_READ 事件,通知 SocketChannel 进行读取。
  9. SocketChannel 读取数据并处理,然后可以注册 OP_WRITE 事件以进行回写。

Java 代码实现:

import java.io.IOException;
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.net.InetSocketAddress;
import java.util.Iterator;
import java.util.Set;public class NioServer {public static void main(String[] args) throws IOException {// 创建 SelectorSelector selector = Selector.open();// 创建 ServerSocketChannel 并绑定端口ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式// 注册 ServerSocketChannel,感兴趣的事件是 OP_ACCEPT(接受连接事件)serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server started on port 8080...");while (true) {// 阻塞,等待事件发生selector.select();// 获取已准备好的事件Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();// 处理 OP_ACCEPT 事件(客户端连接请求)if (key.isAcceptable()) {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept(); // 接受连接clientChannel.configureBlocking(false); // 设置为非阻塞模式System.out.println("Accepted connection from: " + clientChannel.getRemoteAddress());// 注册客户端通道,感兴趣的事件是 OP_READ(读事件)clientChannel.register(selector, SelectionKey.OP_READ);}// 处理 OP_READ 事件(读取客户端数据)if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(256);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭了连接clientChannel.close();} else {// 处理接收到的数据buffer.flip();System.out.println("Received: " + new String(buffer.array(), 0, bytesRead));// 进行数据处理后,可以注册 OP_WRITE 事件发送数据clientChannel.register(selector, SelectionKey.OP_WRITE);}}// 处理 OP_WRITE 事件(写数据回客户端)if (key.isWritable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.wrap("Hello from server!".getBytes());clientChannel.write(buffer);System.out.println("Sent response to client.");// 发送完数据后,可以选择关闭通道或继续读取clientChannel.close();}}}}
}

代码解释:

  1. Selector 和通道初始化

    • 创建了一个 Selector,用于管理 I/O 事件。
    • 使用 ServerSocketChannel 绑定端口 8080,并将其配置为非阻塞模式。
    • 通过 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT) 注册了 OP_ACCEPT 事件,表示服务器正在监听客户端的连接。
  2. 轮询事件

    • 使用 selector.select() 阻塞,等待 I/O 事件的发生。事件发生时,会返回一个包含已就绪事件的集合。
  3. 处理客户端连接请求(OP_ACCEPT)

    • 当客户端连接到服务器时,ServerSocketChannel 会接受连接,并返回一个 SocketChannel,用于与客户端通信。
    • 新建立的 SocketChannel 会注册 OP_READ 事件,表示准备读取客户端的数据。
  4. 处理读事件(OP_READ)

    • 通过 SocketChannel 读取客户端发送的数据,并根据需要进行处理(例如:打印到控制台)。
  5. 处理写事件(OP_WRITE)

    • 将数据写回客户端,完成请求的响应。
  6. 关闭连接

    • 发送完数据后,可以选择关闭通道,释放资源。

UML 时序图

Client ServerSocketChannel Selector SocketChannel 发送连接请求 注册 OP_ACCEPT 事件 轮询到 OP_ACCEPT 事件 接受连接,返回新的 SocketChannel 注册 OP_READ 事件 轮询到 OP_READ 事件 发送数据 读取数据 处理并回写数据 Client ServerSocketChannel Selector SocketChannel

关键点:

  1. 非阻塞 I/O:NIO 的核心在于非阻塞 I/O,通过 select() 轮询多个通道,使得一个线程能够高效地管理多个连接。
  2. 事件驱动模型:通过事件注册和轮询,NIO 可以高效地处理多个客户端连接的 I/O 操作。
  3. 适用场景:适用于高并发的网络服务,如聊天室、游戏服务器等。

总结:

  • NIO 的核心优势在于通过 Selector 的事件轮询,可以减少线程的创建与切换,提高系统并发处理能力。
  • 在 NIO 中,通道与 Selector 的配合使用使得事件处理变得清晰且高效。

http://www.ppmy.cn/news/1539934.html

相关文章

selenium案例——爬取哔哩哔哩排行榜

案例需求&#xff1a; 1.使用selenium自动化爬虫爬取哔哩哔哩排行榜中舞蹈类的数据&#xff08;包括视频标题、up主、播放量和评论量&#xff09; 2.利用bs4进行数据解析和提取 3.将爬取的数据保存在本地json文件中 4.保存在excel文件中 分析&#xff1a; 1.请求url地址&…

ts 中 namespace 作用

一、组织代码和避免命名冲突 1. 代码组织 命名空间允许你将相关的代码分组在一起&#xff0c;使代码结构更加清晰和易于理解。 namespace MathUtils {export function add(a: number, b: number): number {return a b;}export function subtract(a: number, b: number): nu…

根据json转HttpClient脚本

String json “{\n” " “paths”: {\n" " “/dev-api/system/subjectResult/exportUserList”: {\n" " “post”: {\n" " “tags”: [\n" " “bd-subject-result-controller”\n" " ],\n" " “summ…

【数据结构与算法】栈和队列(下)

记录自己所学&#xff0c;无详细讲解 队列的实现--使用动态链表 1.项目目录文件 、 2.头文件 queue.h #pragma once #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <stdbool.h> struct QueueNode {int data;struct Queue…

软件框架和软件架构的概念

软件框架&#xff08;Software Framework&#xff09; 和 软件架构&#xff08;Software Architecture&#xff09; 的关系在于它们分别处于系统设计和实现的不同层次&#xff0c;互为补充。架构决定了系统的整体结构和设计&#xff0c;而框架则是实现这些设计的工具和技术。下…

BMC 中的日志类型:Audit Log、SEL Log、Sys Log 与 SOL Log 的功能与区别

在现代服务器和数据中心管理中&#xff0c;BMC&#xff08;Baseboard Management Controller&#xff09;作为一种关键的管理组件&#xff0c;负责监控和管理硬件设备的状态。为了确保系统的安全性和操作的可追溯性&#xff0c;BMC 提供了多种类型的日志&#xff0c;以记录不同…

C++算法练习-day5——59.螺旋矩阵2

题目来源&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目思路分析 给定一个整数 n&#xff0c;要求生成一个 n x n 的矩阵&#xff0c;其中的元素按螺旋顺序排列&#xff0c;从矩阵的左上角开始&#xff0c;向右、向下、向左、向上依次填充&#xff0c;直到所有元…

PROFINET开发EtherNet/IP开发Vline板卡在称重设备行业的应用

本次分享的&#xff0c;是我们VlinePROFINET开发EtherNet/IP开发嵌入式板卡在称重行业的典型应用。 应用背景 在现代科技高度发达的时代&#xff0c;无论是科学研究、医疗诊断、制药生产还是工业制造&#xff0c;准确的测量和称重都是保证质量和效率的关键。 随着新项目实施…