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集合
  • 使用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 输入输出的更多相关文章

  1. Java NIO技术总结

    一.背景 大家都知道Java BIO,其全称是java blocking IO,相对的Java NIO 全称为java non-blocking IO.顾名思义,java nio 是一种非阻塞IO.N ...

  2. Java IO(3)非阻塞式输入输出(NIO)

    在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...

  3. Java I/O流输入输出,序列化,NIO,NIO.2

    Java IO流 File类: File类是java.io包下代表和平台无关的文件和目录,File不能访问文件内容本身. File类基本操作: System.out.println("判断文 ...

  4. java输入输出 -- Java NIO之选择器

    一.简介 前面的文章说了缓冲区,说了通道,本文就来说说 NIO 中另一个重要的实现,即选择器 Selector.在更早的文章中,我简述了几种 IO 模型.如果大家看过之前的文章,并动手写过代码的话.再 ...

  5. java输入输出 -- Java NIO之套接字通道

    一.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 – 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为Be ...

  6. java输入输出 -- java NIO之文件通道

    一.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...

  7. java输入输出 -- java NIO之缓存区Buffer

    一.简介 java NIO相关类在jdk1.4被引入,用于提高I/O的效率.java NIO包含很多东西,但核心的东西不外乎Buffer.channel和selector.本文先来看Buffer的实现 ...

  8. Java - NIO

    java.nio:NIO-2: NIO 面向流的IO体系一次只能处理一个或多个字节/字符,直至读取所有字节/符,且流中的数据不能前后移动.效率低,当数据源中没有数据时会阻塞线程.Java-4提供的新A ...

  9. JAVA NIO中的Channels和Buffers

    前言 Channels和Buffers是JAVA NIO里面比较重要的两个概念,NIO正是基于Channels和Buffers进行数据操作,且数据总是从Channels读取到Buffers,或者从Bu ...

随机推荐

  1. 行者APP适配国外环境问题解决

    (本文1151字,阅读约5分钟) 玩骑行的同伴都知道,长途骑行,第一需要好的硬件,如大腿发动机.车子.装备等:二是需要好的软件,如意志.有氧能力.骑行app等. 到雅加达后,才发现在国内用了几年的黑鸟 ...

  2. YOLOv4全文阅读(全文中文翻译)

    YOLOv4全文阅读(全文中文翻译) YOLOv4: Optimal Speed and Accuracy of Object Detection 论文链接: https://arxiv.org/pd ...

  3. YOLO v4分析

    YOLO v4分析 YOLO v4 的作者共有三位:Alexey Bochkovskiy.Chien-Yao Wang 和 Hong-Yuan Mark Liao.其中一作 Alexey Bochko ...

  4. pytorch生成对抗示例

    pytorch生成对抗示例 本文对ML(机器学习)模型的安全漏洞的认识,并将深入了解对抗性机器学习的热门话题.图像添加难以察觉的扰动会导致模型性能大不相同.通过图像分类器上的示例探讨该主题.使用第一种 ...

  5. TensorFlow csv读取文件数据(代码实现)

    TensorFlow csv读取文件数据(代码实现) 大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 Tens ...

  6. NSight Compute 用户手册(下)

    主菜单 文件 新建项目使用"新建项目"对话框创建新的分析项目 4. Main Menu and Toolbar Information on the main menu and t ...

  7. Comparison of Laser SLAM and Visual SLAM

    Comparison of Laser SLAM and Visual SLAM 目前,SLAM技术广泛应用于机器人.无人机.无人机.AR.VR等领域,依靠传感器可以实现机器的自主定位.测绘.路径规划 ...

  8. 开源电路分享のFalling Star Board

    设计初衷 想自己做个能连网的时钟,结合RT-thread,显示个天气预报什么的,想想就挺有趣的.考虑到当前的芯片价格,和后续的设计,万一还有个啥奇妙的想法呢,就把这个做成了核心板. 一开始就只做了最小 ...

  9. ContOS8 配置MariaDB

    导语: 该篇文章主要记录ContOS8安装MariaDB后的一些配置内容,若想要详细了解安装过程请移步至上一篇博文! 正文: 首先对MariaDB进行相关的简单配置 使用mysql_secure_in ...

  10. 这 7 个 Linux 命令,你是怎么来使用的?

    使用 Linux 系统的开发者,很多人都有自己喜欢的系统命令,下面这个几个命令令是我平常用的比较多的,分享一下. 我不会教科书般的罗列每个指令的详细用法,只是把日常开发过程中的一些场景下,经常使用的命 ...