NIO入门

IO和NIO的区别:

  1. IO:通过流处理数据,仅支持阻塞IO。
    核心组件:InputStream /OutputStream用于字节的读写,Reader / Writer:用于字符流的读写。读取过程中无法被中断,是阻塞式IO。
  2. NIO:通过管道处理数据,支持阻塞IO和非阻塞IO。
    核心组件:Channel通道、Buffer缓冲区、Selector选择器。Channel与Buffer做直接交互,用于数据的传输,支持非阻塞IO,Buffer用于存放数据,Selector用于管理多个管道,允许单个线程处理多个IO。


NIO三大核心组件

NIO由三大核心组件分别是:Channel管道、Buffer缓冲区、Selector选择器。

Channel

  • 作用:替代传统I/O中的流(Stream),支持双向数据传输(读/写),直接与缓冲区(Buffer)交互。

  • 特点

    • 非阻塞模式(可通过configureBlocking(false)设置)。

    • 操作基于缓冲区,效率更高。

  • 常见实现类

    • FileChannel:文件I/O。

    • SocketChannel / ServerSocketChannel:TCP网络通信。

    • DatagramChannel:UDP网络通信。

Buffer

  • 作用:临时存储数据的容器,是Channel读写数据的直接目标。

  • 核心属性

    • capacity:最大容量。

    • position:当前操作位置。

    • limit:可操作数据的上限。

    • mark:标记位置(用于reset())。

  • 常见类型

    • ByteBuffer(最常用)、CharBufferIntBuffer等。

  • 关键操作

    • put()/get():读写数据。

    • flip():切换为读模式。

    • clear()/compact():清空或压缩缓冲区。

Selector

  • 作用:监听多个Channel的事件(如连接就绪、读就绪、写就绪),实现单线程管理多通道的高并发I/O。

  • 核心机制

    • 基于事件驱动的SelectionKey(包含事件类型和对应的Channel)。

    • 底层依赖操作系统的epoll(Linux)或kqueue(Mac)等系统调用。

  • 使用步骤

    1. 创建Selector:Selector.open()

    2. 将Channel注册到Selector,指定监听事件(如SelectionKey.OP_READ)。

    3. 调用select()方法阻塞等待就绪事件。

    4. 通过selectedKeys()处理就绪的Channel。

 Selector 的 4 种监听事件

事件说明适用 Channel
SelectionKey.OP_ACCEPT服务端接收新连接ServerSocketChannel
SelectionKey.OP_CONNECT客户端连接完成SocketChannel
SelectionKey.OP_READ数据可读SocketChannel / DatagramChannel
SelectionKey.OP_WRITE数据可写SocketChannel / DatagramChannel

Channel与Buffer联调

阻塞IO案例

ServerSocketChannel.accept():该方法用于等待客户端连接,直到有客户端连接才会返回,如果没有客户端连接则会一直阻塞。

SocketChannel.read(ByteBuffer):改方法用于接收客户端的数据,直到有数据才会返回,否则将一直阻塞线程。

    package com.jiawa.netty.server;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.List;import static com.jiawa.netty.utils.ByteBufferUtil.debugAll;public class Server {private static final Logger logger = LoggerFactory.getLogger(Server.class);public static void main(String[] args) throws IOException {//创建缓存、缓存客户端信息ByteBuffer buffer = ByteBuffer.allocate(16);//创建服务器ServerSocketChannel server = ServerSocketChannel.open();//绑定端口server.bind(new InetSocketAddress(8080));//创建客户端存储集合List<SocketChannel> clients = new java.util.ArrayList<>();//接收客户端while (true) {//阻塞线程,直到有客户端请求连接后释放SocketChannel client = server.accept();//如果有客户端请求,则返回客户端信息logger.info("收到客户端请求:{}", client);clients.add(client);for (SocketChannel c : clients) {//读取客户端数据int read = c.read(buffer);if (read > 0) {logger.info("读取客户端数据:{}", read);//打开读buffer.flip();//打印数据debugAll(buffer);//清空buffer.clear();}}}}}

非阻塞IO案例

server.configureBlocking(false):将 ServerSocketChannel 设置为非阻塞模式。

client.configureBlocking(false):将每个 SocketChannel 设置为非阻塞模式。

设置为非阻塞之后,则不会阻塞线程,不管是否有客户端连接或者接收客户端数据,都会直接返回,不会阻塞线程。

package com.jiawa.netty.server;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.List;import static com.jiawa.netty.utils.ByteBufferUtil.debugAll;public class Server {private static final Logger logger = LoggerFactory.getLogger(Server.class);public static void main(String[] args) throws IOException {//创建缓存、缓存客户端信息ByteBuffer buffer = ByteBuffer.allocate(16);//创建服务器ServerSocketChannel server = ServerSocketChannel.open();//非阻塞if (server!= null){server.configureBlocking(false);}//绑定端口server.bind(new InetSocketAddress(8080));//创建客户端存储集合List<SocketChannel> clients = new java.util.ArrayList<>();//接收客户端while (true) {//阻塞线程,直到有客户端请求连接后释放SocketChannel client = server.accept();//将客户端设置为非阻塞if (client != null) {client.configureBlocking(false);}//如果有客户端请求,则返回客户端信息if (client != null) {logger.info("收到客户端请求:{}", client);clients.add(client);}for (SocketChannel c : clients) {//读取客户端数据int read = c.read(buffer);if (read > 0){logger.info("读取客户端数据:{}", read);//打开读buffer.flip();//打印数据debugAll(buffer);//清空buffer.clear();}}}}
}

Channel、Buffer、Selector案例

package com.example.demo.server;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 serverSocketChannel = ServerSocketChannel.open();// 绑定服务器套接字到本地地址和端口8080serverSocketChannel.bind(new InetSocketAddress(8080));// 设置ServerSocketChannel为非阻塞模式serverSocketChannel.configureBlocking(false);// 将ServerSocketChannel注册到Selector上,并监听ACCEPT事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,等待客户端连接...");// 无限循环,持续处理客户端连接和数据读取while (true) {// 阻塞等待感兴趣的事件发生(如新的连接或可读的数据)int readyChannels = selector.select();// 如果没有准备好的通道,则继续下一次循环if (readyChannels == 0) continue;// 获取所有准备就绪的SelectionKey迭代器Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();// 遍历所有准备就绪的SelectionKeywhile (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 检查是否是新的连接请求if (key.isAcceptable()) {// 处理新的连接请求ServerSocketChannel ssc = (ServerSocketChannel) key.channel();// 接受新的客户端连接SocketChannel sc = ssc.accept();// 设置新连接的SocketChannel为非阻塞模式sc.configureBlocking(false);// 将新连接的SocketChannel注册到Selector上,并监听READ事件sc.register(selector, SelectionKey.OP_READ);System.out.println("新的客户端已连接"); // 打印连接信息} else if (key.isReadable()) {// 处理可读事件SocketChannel sc = (SocketChannel) key.channel();// 分配一个新的ByteBuffer,大小为1024字节ByteBuffer buffer = ByteBuffer.allocate(1024);// 从SocketChannel读取数据到ByteBuffer中int bytesRead = sc.read(buffer);// 如果读取到-1,表示客户端断开连接if (bytesRead == -1) {sc.close(); // 关闭SocketChannelSystem.out.println("客户端断开连接"); // 打印断开信息} else {// 切换ByteBuffer到读模式buffer.flip();// 读取ByteBuffer中的数据到byte数组byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);// 将byte数组转换为字符串String message = new String(bytes);System.out.println("收到消息: " + message); // 打印接收到的消息}}// 移除已经处理过的SelectionKeykeyIterator.remove();}}}
}

Channel常用方法:

1. 通用方法(所有Channel实现类共有)

方法说明
boolean isOpen()判断通道是否处于打开状态。
void close()关闭通道,释放资源(必须显式调用,否则可能泄漏资源)。
boolean isConnected()(仅Socket相关Channel)检查通道是否已连接。

2. 可读/可写操作

方法说明
int read(ByteBuffer dst)从通道读取数据到缓冲区,返回实际读取的字节数(非阻塞模式下可能返回0)。
int write(ByteBuffer src)将缓冲区数据写入通道,返回实际写入的字节数(非阻塞模式下可能返回0)。

3. 非阻塞模式控制

方法说明
void configureBlocking(boolean block)设置通道是否为阻塞模式(true为阻塞,false为非阻塞)。
boolean isBlocking()检查通道当前是否为阻塞模式。

4. 网络Channel特有方法(SocketChannel/ServerSocketChannel)

方法说明
SocketChannel bind(SocketAddress local)绑定通道到本地地址(用于服务端)。
SocketAddress getLocalAddress()获取绑定的本地地址。
SocketChannel connect(SocketAddress remote)连接到远程地址(客户端)。
boolean finishConnect()完成非阻塞连接(需配合connect()使用)。
SocketChannel accept()(仅ServerSocketChannel)接受客户端连接,返回一个新的SocketChannel

5. FileChannel特有方法

方法说明
long position()获取当前文件指针位置。
FileChannel position(long newPosition)设置文件指针位置。
long size()返回文件大小。
FileChannel truncate(long size)截断文件到指定大小。
int read(ByteBuffer dst, long position)从文件指定位置读取数据(不移动指针)。
int write(ByteBuffer src, long position)向文件指定位置写入数据(不移动指针)。
long transferTo(long position, long count, WritableByteChannel target)将数据从文件通道直接传输到目标通道(零拷贝优化)。
long transferFrom(ReadableByteChannel src, long position, long count)从源通道直接接收数据到文件通道(零拷贝优化)。
void force(boolean metaData)强制将数据刷到磁盘(确保写入持久化)。

6. DatagramChannel特有方法(UDP)

方法说明
DatagramChannel bind(SocketAddress local)绑定到本地端口。
SocketAddress receive(ByteBuffer dst)接收UDP数据包,返回发送方地址。
int send(ByteBuffer src, SocketAddress target)发送UDP数据包到目标地址。

Buffer常用方法

1. 核心属性相关方法

方法作用说明
int capacity()返回缓冲区容量创建后不可变
int position()返回当前操作位置下一个读写操作的索引
Buffer position(int newPosition)设置当前位置需满足 0 ≤ position ≤ limit
int limit()返回可操作数据上限缓冲区有效数据边界
Buffer limit(int newLimit)设置新的上限需满足 0 ≤ limit ≤ capacity
Buffer mark()标记当前位置配合reset()回溯
Buffer reset()恢复到标记位置未调用mark()会抛异常

2. 模式切换方法

方法作用说明
Buffer flip()写模式 → 读模式limit设为当前positionposition归零
Buffer clear()读模式 → 写模式position=0limit=capacity不清空数据
Buffer rewind()重置为读模式起点position=0limit不变(重复读)
boolean hasRemaining()检查剩余可操作数据return position < limit
int remaining()返回剩余可操作数据量return limit - position

3. 数据读写方法

通用方法(所有Buffer子类)
方法作用
T get()读取当前位置的数据并移动position
Buffer put(T value)写入数据到当前位置并移动position
T get(int index)读取指定位置数据(不移动position
Buffer put(int index, T value)写入数据到指定位置(不移动position
ByteBuffer特有方法
方法作用
byte[] array()返回底层字节数组(仅非直接缓冲区有效)
ByteBuffer put(byte[] src)批量写入字节数组
ByteBuffer get(byte[] dst)批量读取到字节数组
ByteBuffer slice()创建共享底层数据的新缓冲区(切片)
ByteBuffer duplicate()创建完全独立的副本缓冲区

4. 压缩与填充

方法作用典型场景
Buffer compact()压缩缓冲区将未读数据移到起始位置,position指向剩余空间
Buffer fill(byte value)(非标准API,需自行实现)填充固定值到缓冲区

5. 直接缓冲区控制(ByteBuffer特有)

方法作用
static ByteBuffer allocateDirect(int capacity)创建直接缓冲区(堆外内存)
boolean isDirect()判断是否为直接缓冲区

Selector 的常用方法

(1)Selector 的创建与关闭

方法说明
Selector.open()创建 Selector
boolean isOpen()检查 Selector 是否打开
void close()关闭 Selector(释放资源)

(2)Channel 的注册与注销

方法说明
channel.register(selector, ops)注册 Channel 到 Selector,并指定监听事件
key.cancel()取消 Channel 的注册(不会立即注销,需调用 select() 后生效)

(3)事件监听

方法说明
int select()阻塞等待至少一个事件就绪(返回就绪的 Channel 数量)
int select(long timeout)带超时的阻塞等待
int selectNow()非阻塞检查(立即返回)
Set<SelectionKey> selectedKeys()获取所有就绪的事件(需手动处理并移除)
Set<SelectionKey> keys()获取所有已注册的 Channel(包括未就绪的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/41704.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年MathorCup数学建模B题甲骨文智能识别中原始拓片单字自动分割与识别研究解题全过程文档加程序

2024年第十四届MathorCup高校数学建模挑战赛 B题 甲骨文智能识别中原始拓片单字自动分割与识别研究 原题再现&#xff1a; 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文…

【深度学习的数学】导数

导数的定义。好像是从极限开始的。比如说&#xff0c;函数f(x)在点xa处的导数&#xff0c;就是当h趋近于0时&#xff0c;[f(ah) - f(a)]除以h的极限&#xff0c;对吧&#xff1f;公式应该是这样的&#xff1a;f’(a) lim_{h→0} [f(ah) - f(a)] / h。这个极限如果存在的话&…

word文件转换为Markdown格式

目录 一、前言1.1、poi-ooxml、docx4j、aspose-words对比二、poi-ooxml技术实现一、前言 顺应时代技术的变更及高效协同理念的影响,非结构化信息展示、存储、应用等也由传统文档向在线协同文档的演变,类似腾讯在线文档。   目前大多数在线文档支持的是Markdown格式,因此这…

【Hugging Face 开源库】Diffusers 库 —— 扩散模型

Diffusers 的三个主要组件1. DiffusionPipeline&#xff1a;端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE&#xff08;Variational AutoEncoder&#xff09;图像尺寸与 UNet 和 VAE 的关系EMA&#xff08;Exponential Moving…

langserve搭建方法

文章目录 安装 langserver安装 langchain-cli创建langserve脚手架使用poetry管理包 安装 langserver pip install langserve安装 langchain-cli pip install langchain-cli创建langserve脚手架 langchain app new 项目名后续交互界面全回车&#xff0c;接着cd到 项目名 目录…

网络基础-路由器和交换机工作配置

三、路由器和交换机的工作原理配置以及华为体系下的小型网络的搭建 3.1路由基础 3.1.1数据转发 通过链路层交换机和网络层路由器进行数据转发 交换机&#xff08;链路层&#xff09;mac地址表的数据转发路由器&#xff08;网络层&#xff09; ip路由表的数据转发 隔离广播域…

mysql高级,mysql体系结构,mysql引擎,存储过程,索引,锁

1.mysql体系结构 1&#xff09; 连接层 主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作…

Unity高清渲染管线

Unity高清渲染管线——1 unity高清渲染管线是渲染管线的一种&#xff0c;在看完《创造高清3D虚拟世界》这本书的前两章以及第三张第二小节后终于对unity的高清渲染管线也是有了一个初步的认知&#xff0c;以下是我个人理解仅作参考&#xff1a; unity高清渲染管线项目模板比起…

Python基础语法元素(学习笔记)

实例1&#xff1a;温度转换 # TempConvert.py #为单行注释 多行注释为: 这里写内容 TempStr input("请输入带有符号的温度值&#xff1a;") if TempStr[-1] in [F,f] :C (eval(TempStr[0:-1])-32)/1.8print("转换后的温度是{:.2f}C".format(C)) e…

C++20 中的std::c8rtomb和 std::mbrtoc8

文章目录 1. 引言2. std::c8rtomb 函数详解3. std::mbrtoc8 函数详解4. 使用示例5. 注意事项6. 总结 1. 引言 C20 标准引入了对 UTF-8 编码的更好支持&#xff0c;其中包括两个重要的函数&#xff1a;std::c8rtomb 和 std::mbrtoc8。这两个函数分别用于将 UTF-8 编码的字符转换…

数据可视化TensorboardX和tensorBoard安装及使用

tensorBoard 和TensorboardX 安装及使用指南 tensorBoard 和 TensorBoardX 是用于可视化机器学习实验和模型训练过程的工具。TensorBoard 是 TensorFlow 官方提供的可视化工具&#xff0c;而 TensorBoardX 是其社区驱动的替代品&#xff0c;支持 PyTorch 等其他框架。以下是它…

flutter-实现瀑布流布局及下拉刷新上拉加载更多

文章目录 1. 效果预览2. 结构分析3. 完整代码4. 总结 1. 效果预览 在 Flutter 应用开发中&#xff0c;瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时&#xff0c;下拉刷新和上拉加载更多功能&#xff0c;能够极大提升用户体验&#xff0c;让用户方…

【day2】数据结构刷题 栈

一 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的…

YAML是什么?

YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种以数据为中心、高度可读的序列化语言&#xff0c;广泛应用于配置文件、数据交换和自动化工具中。以下从多个维度对其进行全面解析&#xff1a; 1. 定义与历史演变 全称与定位&#xff1a; YAML的全称最初为“Yet…

熔断降级(Sentinel解决)

问题概述 在微服务架构中一定要预防微服务雪崩问题&#xff0c;微服务雪崩问题就是指在微服务架构中&#xff0c;当一个服务出现故障时&#xff0c;由于服务之间的依赖关系&#xff0c;故障可能会传播到其他服务&#xff0c;从而导致了大规模的服务失败&#xff0c;系统无法正…

反序列化漏洞

前提概要 本文章主要用于分享反序列化漏洞基础学习&#xff0c;以下是对反序列化漏洞的一些个人解析&#xff0c;请大家结合参考其他文章中的相关信息进行归纳和补充。 反序列化漏洞描述 反序列化漏洞是指程序在对输入的字节流进行反序列化时&#xff0c;因缺乏充分的验证和过…

吐血整理:Air8201如何使用LuatOS进行电源管理功能!

在物联网应用场景中&#xff0c;设备续航能力直接影响其部署成本与运维效率。LuatOS操作系统通过软件层面的精细化控制&#xff0c;为Air8201提供了灵活且高效的电源管理策略。本文将从系统架构、API接口、实战配置三个维度&#xff0c;解析如何利用LuatOS实现Air8201的智能电源…

STM32学习笔记之存储器映射(原理篇)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

合宙780E开发学习-LUATOS-SOC云编译自定义固件

登录https://luatos.com 点击登录&#xff0c;使用合宙erp账号登录即可 点击右上角构建&#xff0c;点击右上角菜单新构建&#xff0c;自定义构建名称&#xff0c;可新建多个 勾选想要的组件 点击右上角保存修改&#xff0c;只有点击准备就绪&#xff08;注意&#xff1a;一定…

react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析

一、React 15&#xff08;2016&#xff09; 核心架构&#xff1a;Stack Reconciler&#xff08;栈协调器&#xff09; 工作原理&#xff1a; 同步递归渲染&#xff1a;采用深度优先遍历方式递归处理 Virtual DOM&#xff0c;形成不可中断的调用栈渲染流程&#xff1a;1. 触发 …