Java网络编程NIO

一、NIO是什么?

NIO可以说是比BIO更强大的IO,可以设置非阻塞模式(通过事件的方式监听数据的到来)

  • BIO是基于socket通信,一个线程对应一个socket连接,读取数据要一直等待

  • NIO是基于channel通信,一个线程管理一个selector,一个selector管理多个channel,这样一个线程就能管理多个客户端连接

二、NIO核心组件

 ① Selector:选择器,负责管理多个通道channel,通过监听通道绑定的事件,及时处理通道数据

 ② Channel:通道,一般一端确定,另一端操作缓冲区Buffer。可以是文件通道,网络通道等

 ③ Buffer:缓冲区,包括直接内存缓冲和JVM内存缓冲。直接内存缓冲是在操作系统的非应用程序内存申请的空间,不占用JVM内存;JVM内存缓冲就是在堆空间申请的缓冲区。

   由于IO无法直接操作应用内存,因此需要将应用内存数据拷贝到直接内存,如果使用直接内存,就可以少一次拷贝操作

组件模型图:

三、常用API

(1)缓冲区Buffer

① 介绍

  position:下一次读取或写入的位置

  limit:数据末尾后一个位置

  capacitry:缓冲区容量

② 相关API

以ByteBuffer为例

//	申请操作系统的内存缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
//	获取position、limit、capacity
int position = buffer.position();
int limit = buffer.limit();
int capacity = buffer.capacity();//	向缓冲区中写入数据
String data = "hello, my name is jack!";
buffer.put(data.getBytes());//	切换模式,标定界限(limit=position,position=0)
buffer.flip();//	读取缓冲区的数据
int dataLength = buffer.remaining();	//  获取剩余数据量
String content = new String(buffer.array(), 0, dataLength);//	清空缓冲区,实际上是将各个下标设置为初始值,如:position=0,limit=capacity
buffer.clear();

(2)通道Channel

拷贝文件的案例

public void test02() throws Exception {//  利用通道进行文件读写String fileName1 = "E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg";String fileName2 = "E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\2.jpg";//  打开文件输入流FileInputStream fis = new FileInputStream(fileName1);//  通过输入流获取通道FileChannel fisChannel = fis.getChannel();//  打开文件输出流FileOutputStream fos = new FileOutputStream(fileName2);//  通过输出流获取输出通道FileChannel fosChannel = fos.getChannel();//  创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//  将输入通道的数据读取到buffer,然后从buffer读取数据到输出通道while ((fisChannel.read(buffer)) > 0 ) {//  limit=position,position=0buffer.flip();//  将数据从buffer写入到文件fosChannel.write(buffer);//  将各个下标指针归还到最初位置buffer.clear();}//  释放通道资源fisChannel.close();fosChannel.close();}

 利用transferFrom实现管道数据转移

public void transferFromTest() throws Exception {/*利用transferFrom或transferTo实现文件的copy*/FileInputStream fis = new FileInputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg");FileChannel fisChannel = fis.getChannel();FileOutputStream fos = new FileOutputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\11.jpg");FileChannel fosChannel = fos.getChannel();//  将数据转移到目的通道fosChannel.transferFrom(fisChannel , 0, fisChannel.size());fisChannel.close();fosChannel.close();}

利用transferTo实现管道数据转移

public void transferToTest() throws Exception {/*利用transferFrom或transferTo实现文件的copy*/FileInputStream fis = new FileInputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\1.jpg");FileChannel fisChannel = fis.getChannel();FileOutputStream fos = new FileOutputStream("E:\\code\\java\\io_learn\\io_02_nio\\src\\main\\resources\\10.jpg");FileChannel fosChannel = fos.getChannel();//  将数据转移到目的通道fisChannel.transferTo(0, fisChannel.size(), fosChannel);fisChannel.close();fosChannel.close();}

注意:transferFrom和transferTo方法一次只能传输2G的数据(受操作系统限制)

(3)选择器Selector

一般是通过服务通道监听端口,等待客户端的连接通道,然后将连接通道注册到Selector,等待事件触发通道行为

public void test() throws Exception {//  1.获取通道ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();//  2。设置为非阻塞模式serverSocketChannel.configureBlocking(false);//  3.绑定端口serverSocketChannel.bind(new InetSocketAddress(9898));//  4.获取选择器Selector selector = Selector.open();//  5.注册通道到选择器,通过客户端建立通道的事件触发serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (selector.select() > 0) {//  获取到对应事件,进行处理...}}

 四、NIO实现聊天室案例

服务端Server

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class Server {public static void main(String[] args) throws Exception {//  1.创建服务端通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//  2.设置为非阻塞模式serverSocketChannel.configureBlocking(false);//  3.绑定监听端口serverSocketChannel.bind(new InetSocketAddress(9898));//  4.打开SelectorSelector selector =Selector.open();//  5.注册监听客户端连接的通道serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//  6.循环监听连接等事件while (selector.select() > 0) {//  获取事件迭代器Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while (keyIterator.hasNext()) {//  获取到具体事件SelectionKey key = keyIterator.next();//  判断事件类型//  客户端建立连接的事件if (key.isAcceptable()) {//  获取到客户端的连接通道SocketChannel socketChannel = serverSocketChannel.accept();//  设置通道为非阻塞模式socketChannel.configureBlocking(false);//  将连接通道注册到选择器socketChannel.register(selector, SelectionKey.OP_READ);}//  如果是读取事件,证明客户端发来了消息,进行数据读取else if (key.isReadable()) {try {//  创建缓冲区ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//  消息内容,正常聊天,消息一般很短,不会超过1kbString content = "";//  获取到客户端通道SocketChannel channel = (SocketChannel)key.channel();//  通过通道将数据读取到bufferif (channel.read(byteBuffer) > 0) {//  切换为可读模式byteBuffer.flip();//  将byte[]转为stringcontent = new String(byteBuffer.array(), 0, byteBuffer.remaining());//  清空缓冲区byteBuffer.clear();}//  打印消息内容System.out.println(content);//  将数据写入缓冲区byteBuffer.put(content.getBytes());//  将数据发送给其他所有非自己的客户端Set<SelectionKey> keys = selector.keys();for (SelectionKey selectionKey : keys) {//  通过事件获取到客户端连接通道SelectableChannel selectableChannel = selectionKey.channel();//  如果通道是服务端监听连接的通道ServerSocketChannel或者是自己,则不能转发if (selectableChannel instanceof ServerSocketChannel || selectableChannel == channel) {continue;}//  将消息转发给其他客户端//  转为客户端channel类型SocketChannel otherClientChannel = (SocketChannel) selectableChannel;//  重新设置为读模式byteBuffer.flip();//  发送消息otherClientChannel.write(byteBuffer);}} catch (Exception e) {//  出现异常,说明客户端下线SocketChannel channel = (SocketChannel)key.channel();System.out.println("客户端" + channel.getLocalAddress() + "离线");channel.close();//  释放通道,接触通道绑定的事件key.cancel();}}//  将使用完的事件进行移除keyIterator.remove();}}//  释放资源serverSocketChannel.close();selector.close();}}

客户端Client

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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {//  1.创建socket channelSocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));socketChannel.configureBlocking(false); //  设置为非阻塞模式//  2.开启SelectorSelector selector = Selector.open();//  3.注册通道,监听通道的读事件socketChannel.register(selector, SelectionKey.OP_READ);//  开启一个接收服务端数据的线程new Thread(() -> {try {//  有事件,则执行处理,没有事件则进行阻塞while(selector.select() > 0) {Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();//  遍历事件,找到可读事件,读取服务端发来的数据while (keyIterator.hasNext()) {SelectionKey selectionKey = keyIterator.next();//  可读事件,读取服务端发来的数据if (selectionKey.isReadable()) {//  创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//  读取数据到缓冲区socketChannel.read(buffer);//  切换为读模式buffer.flip();System.out.println("\n" + new String(buffer.array(), 0, buffer.remaining()));System.out.print("我的发言: ");}//  使用完毕的事件要进行移除keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}).start();//  等待客户的输入,聊天控制台ByteBuffer buffer = ByteBuffer.allocate(1024);Scanner sc = new Scanner(System.in);while (true) {System.out.print("我的发言: ");//  读取用户输入String msg = socketChannel.getLocalAddress() + ": " + sc.nextLine();//  将消息放入缓冲区buffer.put(msg.getBytes());//  切换为写模式buffer.flip();//  发送缓冲区的数据给服务吨socketChannel.write(buffer);//  清除本次数据buffer.clear();}}
}

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

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

相关文章

【动态规划】二分优化最长上升子序列

最长上升子序列 II 题解 题目传送门&#xff1a;AcWing 896. 最长上升子序列 II 一、题目描述 给定一个长度为 N 的数列&#xff0c;求数值严格单调递增的子序列的长度最长是多少。 输入格式&#xff1a; 第一行包含整数 N第二行包含 N 个整数&#xff0c;表示完整序列 输…

Dify接口api对接,流式接收流式返回(.net)

试了好多种方法除了Console.WriteLine()能打印出来&#xff0c;试了好些方法都不行&#xff0c;不是报错就是打印只有一行&#xff0c;要么就是接收完才返回...下面代码实现调用api接收流式数据&#xff0c;并进行流式返回给前端&#xff1a; using Furion.HttpRemote; using …

19-元素显示模式及浮动(CSS3)

知识目标 掌握标准文档流的解析规则掌握元素的显示模式掌握元素浮动属性语法与使用掌握浮动塌陷解决方法 1. 标准文档流 2. 元素显示模式 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;比如<div>独占一行&#xff0c;一行可以放多…

HTML jQuery 项目 PDF 批注插件库在线版 API 示例教程

本文章介绍 HTML && jQuery Web项目中 PDF 批注插件库 ElasticPDF 在线版 API 示例教程&#xff0c;API 包含 ① 导出批注后PDF数据&#xff1b;② 导出纯批注 json 数据&#xff1b;③ 加载旧批注&#xff1b;④ 切换文档&#xff1b;⑤ 切换用户&#xff1b;⑥ 清空批…

CATIA装配体全自动存储解决方案开发实战——基于递归算法的产品结构树批量处理技术

一、功能定位与技术架构 本工具针对CATIA V5装配体文件管理场景&#xff0c;实现了一套全自动递归存储系统&#xff0c;主要功能包括&#xff1a; ​智能路径选择&#xff1a;通过Tkinter目录对话框实现可视化路径选择​产品结构递归解析&#xff1a;深度优先遍历装配体中的子…

C#:接口(interface)

目录 接口的核心是什么&#xff1f; 1. 什么是接口&#xff08;Interface&#xff09;&#xff0c;为什么要用它&#xff1f; 2. 如何定义和使用接口&#xff1f; 3.什么是引用接口&#xff1f; 如何“引用接口”&#xff1f; “引用接口”的关键点 4. 接口与抽象类的区…

基于卷积神经网络CNN实现电力负荷多变量时序预测(PyTorch版)

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…

关于inode,dentry结合软链接及硬链接的实验

一、背景 在之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里 2.2.3 一节里&#xff0c;我们讲到了file&#xff0c;fd&#xff0c;inode&#xff0c;dentry&#xff0c;super_block这几个概念&#xff0c;在这篇博客里&#xff0c;我们针对inode和dentr…

游戏引擎学习第201天

仓库:https://gitee.com/mrxiao_com/2d_game_5 回顾之前的内容&#xff0c;并遇到了一次一阶异常&#xff08;First-Chance Exception&#xff09;。 欢迎来到新一期的开发过程&#xff0c;我们目前正在编写调试接口代码。 当前&#xff0c;我们已经在布局系统上进行了一些工…

计算机视觉算法实战——基于YOLOv8的行人流量统计系统

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​ ​​​​​​​​​ ​​ 引言:智能客流分析的市场需求 在零售、交通、安防等领域,准确的行人流量统计对于商业决策、公共安全管理…

Redis是什么?架构是怎么样的?

目录 前言 一,Redis架构 1.1 本地缓存 1.2 远程缓存 二,强大的Redis优点 2.1 支持多种数据类型 2.2 内存过期策略 2.3 内存淘汰策略 2.4 持久化 三,Redis是什么 前言 我是一个程序员,维护了一个商品服务,它的背后直连Mysql数据库,假设商品服务对外每秒需要提供1万次…

蓝桥杯真题——传送阵

原题连接&#xff1a;蓝桥杯2024年第十五届省赛真题-传送阵 - C语言网 知识点&#xff1a;并查集 题目描述 小蓝在环球旅行时来到了一座古代遗迹&#xff0c;里面并排放置了 n 个传送阵&#xff0c;进入第 i 个传送阵会被传送到第 ai 个传送阵前&#xff0c;并且可以随时选择…

彩虹表攻击

1. 引言 密码安全一直是信息安全领域的重要课题。攻击者可以利用**暴力破解(Brute-Force Attack)和字典攻击(Dictionary Attack)等方式尝试破解密码。然而,计算机性能的提升使得这些方法的效率不断提高,其中彩虹表攻击(Rainbow Table Attack)**是一种极具威胁性的密码…

Vue2 监听器 watcher

文章目录 前言监听器的作用&#xff1a;工作流程&#xff1a;基本用法1. 简单监听2. 对象形式配置 使用场景1. 执行异步操作2. 监听路由变化3. 复杂对象/数组变化 关键配置项与计算属性的区别动态添加监听器注意事项 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&a…

Linux系统程序设计:从入门到高级Day02

这一篇 我带大家复习一下&#xff0c;C语言中的文件 那一部分 大家注意 这里的图并非原创 是当时我老师的图片 本片作用主要是 后续会有文件相关操作&#xff0c;这篇帮大家复习C语言文件中的内容 有助于大家后面的理解。 文章中代码大多是图片格式&#xff0c;是因为这是我…

N元语言模型的时间和空间复杂度计算

对于N元语言模型&#xff0c;时间复杂度是O(V ^ {N-1})&#xff0c;空间复杂度是O(V ^ {N})&#xff0c;N是词汇表的大小。 空间复杂度&#xff1a;存储所有可能的N-1元组及其对应的词的频次需要大量的存储空间。例如&#xff0c;对于一个三元模型&#xff08;N3&#xff09;&…

Tmux 核心操作速查指南

Tmux 最常用操作笔记 1. 基本概念 会话&#xff08;Session&#xff09;&#xff1a;一个tmux会话可以包含多个窗口&#xff0c;适合长期任务管理。窗口&#xff08;Window&#xff09;&#xff1a;每个窗口是一个独立的终端界面&#xff0c;可包含多个面板。面板&#xff08…

哈希表系列一>两数之和

目录 题目&#xff1a;方法&#xff1a;暴力代码&#xff1a;优化后代码&#xff1a; 题目&#xff1a; 链接: link 方法&#xff1a; 暴力代码&#xff1a; public int[] twoSum(int[] nums, int target) {解法一&#xff1a;暴力解法&#xff1a;int n nums.length;for(int…

端到端机器学习流水线(MLflow跟踪实验)

目录 端到端机器学习流水线(MLflow跟踪实验)1. 引言2. 项目背景与意义2.1 端到端机器学习流水线的重要性2.2 MLflow的作用2.3 工业级数据处理需求3. 数据集生成与介绍3.1 数据集构成3.2 数据生成方法4. 机器学习流水线与MLflow跟踪4.1 端到端机器学习流水线4.2 MLflow跟踪实验…

英语学习:读科技论文的难处

如果读起科技论文&#xff0c; 我们就知道自己到底欠缺什么知识了&#xff0c; 那是一个挨着一个的缺。 而且还没有维基百科可用。 怎么办&#xff1f;没办法&#xff01;硬看&#xff01; 而且还要面临语言的差异性困难。比如这一句怎么翻译比较合适&#xff1f;还是直接不翻译…