NIO的浅了解

一、五种IO类型

1、阻塞IO

用户进程一直等待数据准备好,在复制完成之前都是阻塞的

2、非阻塞IO

用户进程需要不断轮询查看是否数据准备好

优化了提升并发连接数量,但是每一个请求都需要创建一个socket建立连接,每个线程都需要去遍历轮询,导致cpu的消耗,直到数据返回成功

3、IO复用

(1)select/poll

单个线程可以处理多个网络连接。相对于BIO多了一步,注册到selector的过程,进程被selector函数阻塞。

select单个进程所能打开的fd()【就是文件描述符,linux内把所有的外部设备都看成一个文件操作】,对于每一个文件的读写操作都会调用内核提供的系统命令来返回给fd,对于socket的读写也会返回一个fd,所以放fd的时候,说明数据是可读或者是可写。

所以这个模型在单进程里,操作的连接数默认是1024,可以修改,但是会带来网络性能的下降,因为会去加大轮询次数,带来网络延迟,因为只有少数连接处于活跃状态,而每次轮询是查询所有的连接。

jdk1.6之前是使用这种模型

(2)epoll

jdk1.6后使用此模型。解决select-poll的缺陷

  • 对单个进程锁打开的连接数没有限制(连接需要占用内存,1TB大概10w个连接)
  • 利用每个fd上的callback函数来实现异步回调,省去了轮询的开销
  • mmap:通过内核和用户空间映射同一块内存空间,来减少内存复制

4、异步IO

二、NIO

1、了解

基于通道和缓冲区操作的:

  • 通道:一个新的原始IO
  • 缓冲支持:负责数据存储和传输的缓冲区
  • 具体:数据从通道读取到缓冲,数据从缓冲区写入通道

非阻塞的:

  • 针对网络的,当线程从通道读取数据到缓冲区,线程依然可以进行其他事情(1.6以后epoll模型)

选择器:

  • 用于监听多个通道的事件,例如链接打开、数据到达
  • 单个线程可以监听多个数据通道

2、Channel

FileChannel:从文件中读写数据(不适合Selector,因为不能非阻塞)

DatagramChannel:通过UDP协议读写网络中的数据

SocketChannel:通过TCP协议读写网络中的数据

ServerSocketChannel:监听一个TCP连接,对于每一个新的客户端连接都会创建一个SocketChannel。

3、Buffer

buffer是一个对象,包含需要写入或者刚读出的数据,常用的缓冲区类型是ByteBuffer

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            FileInputStream fis = new FileInputStream(new File("D:/test.txt"));
            FileOutputStream fos = new FileOutputStream(new File("D:/test.txt"));
            FileChannel fin = fis.getChannel();
            FileChannel fout = fos.getChannel();

            //初始化一个缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //读取数据到缓冲区
            fin.read(buffer);
            //从读转化为写
            buffer.flip();
            fout.write(buffer);
            //重置缓冲区
            buffer.clear();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

4、IO和NIO的区别

类型操作区域处理数据IO
IO面向最初的数据源

每次读取时=读取所有字节或字符,无缓存

无法前后移动读取流中的数据

当一个线程在读或者写的时候,当数据被完全读取/写入之后,并且数据未准备好时,线程不能做其他任务,只能一直等待。

当线程处于活跃状态并且外部未准备好时,阻塞。

NIO面相缓冲区

先将数据读取到缓冲区

可在缓冲区前后移动数据流

当一个线程向某个通道发送请求时,当数据被完全读取/写入,并且数据未准备好时,线程可以操作其他任务,直到数据准备后再切换回原通道,继续读写,也就是selector的使用。

外部准备好时才唤醒线程,则不会阻塞。

5、缓冲区的内部细节

缓冲区本质是一块可以写入数据,以及从中读取数据的内存,实际上也是一个byte[]数据,只是在NIO中被封装成了NIO Buffer对象,并提供了一组方法来访问这个内存块。

- capacity:容量

- position:位置。读是跟踪从缓冲区读取了多少数据;写是数据放入数组哪个位置;

- limit:写是还有多少数据取出来写到通道;读是还有多少空间可以放出去;

例如:写入

初始话时,capacity是8,就是一个8长度的格子,当写入“hello”的时候,就是ficn.reds("hello);时,postion和limit都会指向4(从0开始),当flip()时,position指向0,limit是4,0-4就是要写入的字符,最后write()写入。

6、零拷贝的原理

IO流程:内核给磁盘发送命令需要读取磁盘的数据,在DMA的控制下,把磁盘上的数据读入到内核缓冲区,内核把数据从内核缓冲区复制到用户缓冲区。用户缓冲区再将数据拷贝到Socket buffer(也是内核),最后发送到网卡缓冲区,四个步骤。

设计到一个用户态到内核态的切换,影响cpu的性能。

从内核态到用户缓冲区没有用,为什么要设计呢

为了提升IO性能,假设应用程序进行读,内核缓冲区对于读相当于一个缓冲空间,当用户只读取一小部分数据的时候,但是内核从磁盘会读取一块数据,下次用户再读其他的数据的时候,在内核缓冲区已经存在就不需要再去磁盘获取,从这个角度是提升了性能的。

使用内存映射缓存mmap,通过内核和用户空间映射同一块内存空间,来减少内存复制。

零拷贝就是减少拷贝次数,减少内核态和用户态之间的数据的复制,提升IO性能。

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            FileChannel in = FileChannel.open(Paths.get("D:/logo.png"), StandardOpenOption.READ);
            FileChannel out = FileChannel.open(Paths.get("D:/logo_cp.png"), StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.WRITE);

            MappedByteBuffer inMap = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
            MappedByteBuffer outMap = in.map(FileChannel.MapMode.READ_WRITE, 0, in.size());

            byte[] bytes = new byte[inMap.limit()];
            inMap.get(bytes);
            outMap.put(bytes);

            in.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

public class ccc {
    public static void main(String[] args) {
        try {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("localhost", 9090));
            FileChannel channel = new FileInputStream("D:/1.txt").getChannel();
            int position = 0;
            long size = channel.size();
            while (size > 0) {
                long tf = channel.transferTo(position, size, socketChannel);
                if (tf > 0) {
                    position += tf;
                    size = tf;
                }
            }
            socketChannel.close();
            channel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class ddd {
    public static void main(String[] args) {
        try {
            ServerSocketChannel channel = ServerSocketChannel.open();
            channel.socket().bind(new InetSocketAddress(9090));
            SocketChannel accept = channel.accept();
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            int r = 0;
            FileChannel fileChannel = new FileOutputStream("D:/text_cp.txt").getChannel();
            while (r != -1){
                r = accept.read(allocate);
                allocate.flip();
                fileChannel.write(allocate);
                allocate.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

7、SocketChannel和ServerSocketChannel

服务端

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            //支持两种模式:阻塞、非阻塞
            ServerSocketChannel open = ServerSocketChannel.open();
            open.configureBlocking(false);
            //绑定端口号
            open.socket().bind(new InetSocketAddress(9090));
            while (true) {
                SocketChannel accept = open.accept();
                //存在连接
                if (accept != null) {
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    accept.read(buffer);
                    System.out.println(new String(buffer.array()));
                    //再把消息写回到客户端
                    Thread.sleep(10000);
                    buffer.filp();
                    accept.write(buffer);
                } else {
                    Thread.sleep(1000);
                    System.out.println("无客户端连接");
                }
            }
        } catch (FileNotFoundException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

客户端

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            SocketChannel open = SocketChannel.open();
            //把客户端设置为非阻塞,在非阻塞模式下,不一定是等到连接建立之后再往下执行
            open.configureBlocking(false);
            open.connect(new InetSocketAddress("localhost", 9090));
            if(open.isConnectionPending()) {
                open.finishConnect();
            }
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.flip();
            open.write(buffer);
            //读取服务端返回的数据
            buffer.clear();
            //非阻塞模式,这里不阻塞
            int r = open.read(buffer);
            if(r > 0) {
                System.out.println("收到服务端的消息" + new String(buffer.array()));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

8、选择器Selector

一个单独的线程可以管理多个channel,从而管理多个网络连接。

public class aaa {

    static Selector selector;

    public static void main(String[] args) throws IOException {
        //创建一个多路复用器
        Selector selector = Selector.open();
        ServerSocketChannel channel = ServerSocketChannel.open();
        //连接的非阻塞
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(9090));
        //监听连接事件,会返回一个SelectionKey,通道的唯一标识
        channel.register(selector, SelectionKey.OP_ACCEPT);
        //轮询
        while (true) {
            //阻塞,所有注册到复用器上事件
            selector.select();
            //一旦某个channel准备就绪,就返回他们的key
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //一定是已经就绪的通道
                SelectionKey next = iterator.next();
                //拿到通道后,可以处理了,避免重复处理
                iterator.remove();
                if(next.isAcceptable()){
                    //连接事件
                    HandleAccept(next);
                }else if(next.isReadable()){
                    //读事件
                    HandleRead(next);
                }
            }
        }

    }

    private static void HandleAccept(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
        SocketChannel accept = serverSocketChannel.accept();
        //IO的非阻塞
        accept.configureBlocking(false);
        accept.write(ByteBuffer.wrap("Server write".getBytes()));
        //注册的是accept的读事件
        accept.register(selector, SelectionKey.OP_ACCEPT);
    }

    private static void HandleRead(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.read(buffer);
        System.out.println("Server receive msg: " + new String(buffer.array()));

    }
}
public class bbb {
    static Selector selector;

    public static void main(String[] args) {
        try {
            selector = Selector.open();
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 9090));
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (true) {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    iterator.remove();
                    if(next.isConnectable()){
                        //连接事件
                        HandleConnect(next);
                    }else if(next.isReadable()){
                        //读事件
                        HandleRead(next);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void HandleConnect(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        if(socketChannel.isConnectionPending()){
            socketChannel.finishConnect();
        }
        socketChannel.configureBlocking(false);
        socketChannel.write(ByteBuffer.wrap("Client receive".getBytes()));
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    //读取服务端返回的数据
    private static void HandleRead(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.read(buffer);
        System.out.println("Client receive msg: " + new String(buffer.array()));
    }
}

9、BIO\NIO\AIO的区别

BIONIOAIO
阻塞阻塞:一个线程执行IO操作会被阻塞非阻塞:线程可以同时处理多个IO请求非阻塞
同步同步:需要等待IO操作完成后才能继续执行异步:轮询方式=channel+buffer+selector回调机制
处理    面向流缓冲区面向事件
并发低,需要创建大量线程通过单线程或少量线程处理同左
场景连接数少且吞吐量不高连接数较多但请求量较小高并发

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数字三角形模型 笔记

方格取数 走两次的最大值 f[k][i1][i2]来表示 k i1 j1 i2 j2; 每一个状态可由四种状态转换来&#xff0c;分别为 第一条路走下&#xff0c;第二条路走下 第一条路走下&#xff0c;第二条路走右 第一条路走右&#xff0c;第二条路走下 第一条路走右&#xff0c;第二条…

三国杀中的概率学问题4——曹冲

前言 这篇文章是围绕曹冲的称象技能展开的一些数学上的讨论&#xff0c;将涉及到积分、概率论等知识&#xff0c;并会做很多拓展。 值得说明的是&#xff0c;本文受到了这篇文章的一些启发。 连续情形1 先来看一个连续情形的问题。 问题一&#xff1a;假设每张牌的点数是0~1…

力扣刷题-二叉树-对称二叉树

101 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 思路 我的思路…

Unity--互动组件(Button)

1.组件的可交互 2.组件的过渡状态 3.组件的导航 4.组件的Event Button “”组件的可交互&#xff1a;“” Interactable&#xff1a; 该组件是否可点击&#xff08;设置为false时&#xff0c;将禁用交互&#xff0c;并且过渡状态将设置为禁用状态&#xff09;&#xff1b;…

深入理解C++关联式容器:set、multiset、map和multimap详解

序列式容器 与 关联式容器 我们知道&#xff1a; C 中&#xff0c;我们将 vector、list、queue 这种底层为线性序列的数据结构叫做 序列式容器&#xff0c;其存储的就是元素本身。而 关联式容器 以键-值对的形式存储数据。每个键在容器中必须是唯一的&#xff0c;而值则与相应…

Windows没有USB启动选项很常见,但解决方法更常见

当试图在计算机上重新安装Windows 11/10操作系统,或从安装介质启动时,一些用户看到错误–系统没有任何USB启动选项,请在启动管理器菜单中选择其他启动选项。此错误出现在不同OEM的多个设备,原因包括启用了安全引导、禁用了Legacy/CSM支持、联想服务引擎、未正确制作可引导U…

本地化小程序运营 同城小程序开发

时空的限制让本地化的线上平台成为一种追求&#xff0c;58及某团正式深挖人们城镇化、本地化的信息和商业需求而崛起的平台&#xff0c;将二者结合成本地化小程序&#xff0c;显然有着巨大的市场机会。本地化小程序运营可以结合本地化生活需求的一些信息&#xff0c;以及激发商…

linux下使用Docker Compose部署Spug实现公网远程访问

&#x1f4d1;前言 本文主要是linux下使用Docker Compose部署Spug实现公网远程访问的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &am…

vcomp120.dll丢失怎么办?vcomp120.dll丢失的解决方法分享

vcomp120.dll丢失”。这个错误通常会导致某些应用程序无法正常运行&#xff0c;给用户带来困扰。那么&#xff0c;当我们遇到这个问题时&#xff0c;应该如何修复呢&#xff1f;下面我将为大家介绍四个修复vcomp120.dll丢失的方法。 一、使用dll修复程序修复 可以通过百度或许…

【PWN · heap | unlink | free_hook】[SUCTF 2018 招新赛]unlink

在前期学习了unlink后&#xff0c;今天翻NSSCTF找到一道名为unlink的题目&#xff0c;尝试不看wp做。过程很顺利&#xff01; 前言 题目对于知识点unlink还是非常裸的&#xff0c;很直接&#xff0c;思路很清晰。 一、题目 二、思路浅析 通过对该程序的反编译&#xff0c;我们…

前端案例-css实现ul中对li进行换行

场景描述&#xff1a; 我想要实现&#xff0c;在展示的item个数少于4个的时候&#xff0c;则排成一行&#xff0c;并且均分&#xff08;比如说有3个&#xff0c;则每个的宽度为33.3%&#xff09;&#xff0c;如果item 个数大于4&#xff0c;则进行换行。 效果如下&#xff1a…

4.0 Linux进程前导知识

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 冯.诺依曼体系 CPU&#xff1a;运算器&#xff0c;控制器 输入设备&#xff1a;键盘&#xff0c;麦克风&#xff0c;摄像头&#xff0c;鼠标&#xff0c;网卡&#xff0c;磁盘等。 输出设备&#xff1a;显示器&#xff0…

KMP算法理论

KMP算法理论 前缀&#xff1a;包含首字母不包含尾字母的都称为前缀 例如 前缀 后缀&#xff1a;只包含尾字母不包含首字母的的称为后缀 后缀 寻找最长相等的前缀和后缀 前缀表 所谓next数组就是前缀表&#xff0c;在遇到冲突时next数组会告诉我们要回退到哪里 next数组的不同…

Java基础-基础语法

1、概述 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff0c;它的状态有&#xff1a;颜色、名字、品种&#xff1b;…

No189.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

十三、W5100S/W5500+RP2040树莓派Pico<FTP Server>

文章目录 1. 前言2. 相关简介2.1 简述2.2 原理2.3 优点2.4 应用 3. WIZnet以太网芯片4. FTP Server运行测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 在当今的信息化时代&#xff0c;互联网已经成为人们生活、工作不可…

轻量级 SSO 方略:基于 OIDC 规范(二)

上一篇文章介绍了 SSO 相关的基础数据&#xff0c;这样有了 ClientId 和密钥后&#xff0c;我们就要准备客户端这边的代码。客户端当前指的便是一个网站&#xff08;也就是 RP&#xff09;&#xff0c;这个网站要求有会员功能&#xff0c;典型地网站导航上通常会有“注册”或“…

深度学习之基于YoloV5电梯电动车预警系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习在电梯电动车预警系统中的应用是一个复杂的系统工程&#xff0c;涉及计算机视觉、机器学习、深度学习等领域…

CLIP:万物分类(视觉语言大模型)

本文来着公众号“AI大道理” ​ 论文地址&#xff1a;https://arxiv.org/abs/2103.00020 传统的分类模型需要先验的定义固定的类别&#xff0c;然后经过CNN提取特征&#xff0c;经过softmax进行分类。然而这种模式有个致命的缺点&#xff0c;那就是想加入新的一类就得重新定义…

14:00面试,14:08就出来了,问的问题有点变态

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%,…