NIO 输入输出
NIO 是java14 API 提供的一种新输入输出流,一套用于标准IO的文件读写,一套用于网络编程。
1. NIO 与IO 的区别
- IO流以字节流输入输出,一次以一个字节进行数据操作,效率慢; NIO以块的方式处理数据,面向缓冲区,一次产生或消费一个数据块,速度快
- IO流是阻塞IO,当客户端连接会处于阻塞状态;NIO是非阻塞IO,可以设置属性进行多路复用处理
- NIO可以通过选择器Selctors进行多路复用
2. 核心组件
- ByteBuffer 数据缓冲区
- 对数据进行处理,本质是一个数组,用来保存类型不同的数据
- Buffer是一个抽象类,有很多子类,最常用的是ByteBuffer
- 创建(静态方法调用)
- (最常用)在堆中创建缓冲区 allocate(int capacity)
- 在系统内存创建 allocateDirect(int capacity)
- 普通数组创建 wrap(byte[] arr)
- 常用方法
- 添加元素,写处理 put(byte b)
- 获取元素,读处理 get(byte b)
- 将缓冲区转换成数组 对象名.array()
- 切换读模式,将position=0;limit=position flip()
- 清空数据 clear(),数据只是被遗忘,但仍存在在缓冲区,不能在通过下标获取元素,position=0;limit=capacity
- 重要属性
- capacity 容量,缓冲区能容纳的最大数据元素数量,一经设定不可改变
- limit 界限,缓冲区可以操作的数据大小,limit后面的元素都不可操作,初始化为capacity的值
- position 位置,下一个要被操作的数据位置,初始化为0, put 和get 操作都会影响limit和position的值
- mark 标记,初始化为-1,记录上一次position的位置
package task04; import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.util.Arrays; public class ByteBufferTest {
public static void main(String[] args) {
//创建
ByteBuffer bf1=ByteBuffer.allocate(10);
ByteBuffer bf2=ByteBuffer.allocateDirect(20);
byte[] bytes=new byte[1024];
ByteBuffer bf3=ByteBuffer.wrap(bytes);
//属性值
System.out.println(bf1.capacity()); //10
System.out.println(bf1.limit());//10
System.out.println(bf1.position());//0
System.out.println(bf1.mark());//java.nio.HeapByteBuffer[pos=0 lim=10 cap=10]
System.out.println("-----------"); //添加获取元素
byte[] arr=new byte[]{1,2,3,4,5};
bf1.put(arr);
System.out.println(bf1.get());//0 因为此时position在5的位置
bf1.flip();
System.out.println(bf1.get());//1,在ByteBuffer 下标为0
System.out.println(bf1.get(2));//3,在ByteBuffer 下标为2,这里并不会设置position的值
System.out.println(bf1.get());//2 position=2
System.out.println(bf1.get());//3 position=2 System.out.println("-----------");
//属性值
System.out.println(bf1.capacity());//10
System.out.println(bf1.limit());//6
System.out.println(bf1.position());//3 }
}
- Channel通道
- 用于传输数据,与数据处理无关,双向传输,在多路复用时要进行注册操作
- Channel接口通过以下实现类实现
- FileChannel:用于读取、写入、映射和操作文件的通道。
package task04; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel; public class FileTest {
public static void main(String[] args) throws IOException {
//建立输入输出流
FileInputStream fis=new FileInputStream("D:\\a.txt");
FileOutputStream fos=new FileOutputStream("D:\\b.txt");
//获取连接
FileChannel fc1=fis.getChannel();
FileChannel fc2=fos.getChannel();
//缓冲区
ByteBuffer bf=ByteBuffer.allocate(1024); //读取
while (fc1.read(bf)!=-1){
bf.flip();
fc2.write(bf);
bf.clear();
}
//关流
fis.close();
fos.close(); }
}- DatagramChannel:通过 UDP 读写网络中的数据通道。
- SocketChannel:通过 TCP 读写网络中的数据。
- ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来 的连接都会创建一个SocketChannel
- 网络通信常用open方法生成实例对象
- SocketChannel:通过connect方法建立连接
- ServerSocketChannel:通过bind方法绑定端口号,accept方法建立连接
- 设置非阻塞方式:configureBlocking(false)
package task04; import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
//客户端
public class ClientTest {
public static void main(String[] args) throws Exception {
SocketChannel sc = SocketChannel.open();
System.out.println("建立连接");
sc.connect(new InetSocketAddress("127.0.0.1",9999));
System.out.println("成功连接服务端");
//创建缓冲区
ByteBuffer bf=ByteBuffer.allocate(1024);
bf.put("哈哈哈".getBytes());
bf.flip();
sc.write(bf);
//关闭
sc.close();
}
}package task04; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
//服务器端
public class ServerTest {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(9999));
System.out.println("等待连接");
//设置非阻塞
ssc.configureBlocking(false); while (true){
SocketChannel accept = ssc.accept(); //阻塞IO
if(accept!=null){
System.out.println("连接成功");
//读取数据
ByteBuffer bf=ByteBuffer.allocate(1024);
int read = accept.read(bf);
System.out.println(new String(bf.array(),0,read));
accept.close();
ssc.close();
break;
}else {
System.out.println("嘻嘻嘻");
Thread.sleep(2000);
} } }
} - Selector 选择器,多路复用器
- 一个选择器可以同时监听多个服务器端口,帮多个服务器端口同时等待客户端访问
- Selector是channel通道的多路复用器,同时监控多个通道的IO状况,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力。Selector 允许单线程处理多个Channel。仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道,这样会大量的减少线程之间上下文切换的开销。
- 可选择通道SelectableChannel ,继承了抽象类SelectableChanne的Channel可以被复用,否则不能
- 通道和选择器之间的关系,使用注册的方式完成。SelectableChannel可以被注册到Selector对象上,在注册的时候,需要指定通道的哪些操作,是Selector感兴趣的
- Channel.register(Selector sel,int ops)方法,将一个通道注册到一个选择器时。
- 第一个参数:指定通道要注册的选择器是谁
- 第二个参数:指定选择器需要查询的通道操作
- 可读 : SelectionKey.OP_READ
- 可写 : SelectionKey.OP_WRITE
- 连接 : SelectionKey.OP_CONNECT
- 接收 : SelectionKey.OP_ACCEPT
- select() :选择器等待客户端连接的方法
- selectedKeys() :选择器会把被连接的服务端对象放在Set集合中,这个方法就是返回一个Set集合
- Channel.register(Selector sel,int ops)方法,将一个通道注册到一个选择器时。
- 使用selector
- 创建Selector,Selector对象是通过调用静态工厂方法open()来实例化的
- 创建channel,与Selector一起使用时,Channel必须处于非阻塞模式下,否则将抛出异常iIlegalBlockingModeException
- 设置非阻塞
- 绑定连接
- 制定监听事件
- 通过Selector的 select() 方法,可以查询出已经就绪的通道操作,这些就绪的状态集合,包存在一个元素是SelectionKey对象的Set集合中。select()方法返回的int值,表示有多少通道已经就绪
package task04; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set; public class SelectServer {
public static void main(String[] args) throws IOException {
//创建服务端对象
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ServerSocketChannel ssc3 = ServerSocketChannel.open();
//设置非阻塞
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
ssc3.configureBlocking(false);
//创建服务端对象
ssc1.bind(new InetSocketAddress(9999));
ssc2.bind(new InetSocketAddress(8888));
ssc3.bind(new InetSocketAddress(7777)); //创建选择器对象
Selector selcetor = Selector.open();
//两个服务器都要交给选择器来管理
ssc1.register(selcetor, SelectionKey.OP_ACCEPT);
ssc2.register(selcetor, SelectionKey.OP_ACCEPT);
ssc3.register(selcetor, SelectionKey.OP_ACCEPT); while (selcetor.select() > 0) {
//获取集合
//selectedKeys() :返回集合,集合作用存放的是被连接的服务对象的key
Set<SelectionKey> selectionKeys = selcetor.selectedKeys();
// 6、采用轮询的方式,查询获取"准备就绪"的注册过的操作
// 7、获取当前选择器中所有注册的选择键(“已经准备就绪的操作”)
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 8、获取"准备就绪"的事件
while (iterator.hasNext()) {
SelectionKey next = iterator.next();
// 9、获取ServerSocketChannel
ServerSocketChannel channel = (ServerSocketChannel) next.channel();
// 10、接受客户端发来的数据
SocketChannel accept = channel.accept();
ByteBuffer bf = ByteBuffer.allocate(1024);
// 11、读取数据
int length = 0;
while ((length = accept.read(bf)) != -1) {
bf.flip();
System.out.println(new String(bf.array(), 0, length));
bf.clear();
}
accept.close();
}
// 12、移除选择键
iterator.remove();
}
// 13、关闭连接
ssc1.close();
ssc2.close();
ssc3.close(); }
}
package task04; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel; public class SelectClient {
public static void main(String[] args) throws IOException {
//创建客户端
SocketChannel sc=SocketChannel.open();
//指定要连接的服务器ip和端口
sc.connect(new InetSocketAddress("127.0.0.1",9999));
//创建缓冲输出
ByteBuffer bf=ByteBuffer.allocate(1024);
//给数组添加数据
bf.put("hahaha".getBytes());
//切换
bf.flip();
//输出数据服务端
sc.write(bf);
//关闭资源
sc.close();
} }
NIO 输入输出的更多相关文章
- Java NIO技术总结
一.背景 大家都知道Java BIO,其全称是java blocking IO,相对的Java NIO 全称为java non-blocking IO.顾名思义,java nio 是一种非阻塞IO.N ...
- Java IO(3)非阻塞式输入输出(NIO)
在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...
- Java I/O流输入输出,序列化,NIO,NIO.2
Java IO流 File类: File类是java.io包下代表和平台无关的文件和目录,File不能访问文件内容本身. File类基本操作: System.out.println("判断文 ...
- java输入输出 -- Java NIO之选择器
一.简介 前面的文章说了缓冲区,说了通道,本文就来说说 NIO 中另一个重要的实现,即选择器 Selector.在更早的文章中,我简述了几种 IO 模型.如果大家看过之前的文章,并动手写过代码的话.再 ...
- java输入输出 -- Java NIO之套接字通道
一.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 – 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为Be ...
- java输入输出 -- java NIO之文件通道
一.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...
- java输入输出 -- java NIO之缓存区Buffer
一.简介 java NIO相关类在jdk1.4被引入,用于提高I/O的效率.java NIO包含很多东西,但核心的东西不外乎Buffer.channel和selector.本文先来看Buffer的实现 ...
- Java - NIO
java.nio:NIO-2: NIO 面向流的IO体系一次只能处理一个或多个字节/字符,直至读取所有字节/符,且流中的数据不能前后移动.效率低,当数据源中没有数据时会阻塞线程.Java-4提供的新A ...
- JAVA NIO中的Channels和Buffers
前言 Channels和Buffers是JAVA NIO里面比较重要的两个概念,NIO正是基于Channels和Buffers进行数据操作,且数据总是从Channels读取到Buffers,或者从Bu ...
随机推荐
- PHP转JAVA开发30分钟实战攻略
服务端开发中,有很多知识是相通的,例如mysql,redis,http协议等. 基于这些基础,在编程语言上的转变并不困难. 本文主要从下面几点出发,讲述如何快速从php开发转为java开发: 使用框架 ...
- MongoDB(13)- 查询操作返回指定的字段
插入测试数据 db.inventory.insertMany( [ { item: "journal", status: "A", size: { h: 14, ...
- 解决mysql无法远程连接的问题
前言 最近开发中遇到一个问题,mysql在服务器本地可以登录,但是远程通过3306端口却不可以.这个问题困扰了我一周之久,终于在今天解决了.在解决的过程中试了很多的方法,遂记录下来,希望能给大家一些提 ...
- grasshopper之python电池执行逻辑
在grasshopper中,需要导入的包虽然不多,但是相当绕人,所要实现的操作往往找不到,暂时做个分类. 双击输入 python 电池: # 导入rhino 包 import Rhino #Rhino ...
- 面阿里P7,竟问这么简单的题目?
关于作者:程序猿石头(ID: tangleithu),来自十八县贫困农村(查看我的逆袭之路),BAT某厂P7,是前大疆(无人机)技术主管,曾经也在创业公司待过,有着丰富的经验. 本文首发于微信公众号, ...
- TVM 高效保护隐私 ML
TVM 高效保护隐私 ML 这篇文章描述了Myelin,一个在值得信赖的硬件飞地中保护隐私的机器学习框架,以及TVM如何使Myelin快速.关键的想法是,TVM,不像其它流行的ML框架,将模型编译成轻 ...
- CUDA运行时 Runtime(四)
CUDA运行时 Runtime(四) 一. 图 图为CUDA中的工作提交提供了一种新的模型.图是一系列操作,如内核启动,由依赖项连接,依赖项与执行分开定义.这允许定义一次图形,然后重复启动.将 ...
- MLPerf Inference 0.7应用
MLPerf Inference 0.7应用 三个趋势继续推动着人工智能推理市场的训练和推理:不断增长的数据集,日益复杂和多样化的网络,以及实时人工智能服务. MLPerf 推断 0 . 7 是行业标 ...
- P4779 【模板】单源最短路径(标准版)单源最短路Dijkstra
题目描述 给定一个$n$个点,$m$条有向边的带非负权图,请你计算从$s$出发,到每个点的距离. 数据保证你能从$s$出发到任意点. 输入格式 第一行为三个正整数$n,m,s$. 第二行起$m$行,每 ...
- 2、java基础语法(上):变量与运算符
关键字与保留字 关键字 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词) 特点:关键字中所有字母都为小写 官方地址:https://docs.oracle.com/javase/tut ...