Netty教程之NIO基础

news2024/10/7 14:22:18

NIO

介绍

NIO 全称java non-blocking IO(非阻塞 I/O),后续提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 New IO),是同步非阻塞的。

        阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪的一种处理方式,当数据没有准备的时候。

阻塞(Block):往往需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里。

非阻塞(Non-Block):当我们的进程访问我们的数据缓冲区的时候,如果数据没有准备好则直接返回,不会等待。如果数据已经准备好,也直接返回

         同步和异步都是基于应用程序和操作系统处理 IO 事件所采用的方式;

同步:应用程序要直接参与 IO 读写的操作,必须阻塞在某个方法上面等待我们的 IO 事件完成

异步:所有的 IO 读写交给操作系统去处理,应用程序只需要等待通知,可以去做其他的事情,并不需要去完成真正的 IO 操作,当操作完成 IO 后,会给我们的应用程序一个通知

特点

1.非阻塞式的I/O操作。这意味着一个线程可以同时管理多个连接,而不必等待每个连接的I/O操作完成

2.通过Channel和Buffer来进行数据传输。Channel表示与实体(文件、套接字等)的连接,而Buffer是用于在Channel和应用程序之间传输数据的缓冲区

3.提供了内存映射文件的功能,可以将文件直接映射到内存中,从而实现了快速的文件I/O操作

4.提供了灵活的缓冲区管理功能,可以方便地进行数据的读取、写入和处理

5.采用了面向块的数据传输方式,可以一次性传输大量数据,提高了I/O操作的效率

运用场景

        适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕系统,服务器间通讯等

三大组件

NIO的三个最重要的核心分别为:Channel,Buffer和Selector

Channel(通道)

        通道;对原 I/O 包中的流的模拟,到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象,通道是双向的(一个Channel既可以读数据,也可以写数据)

常见类型:

        FileChannel

        DatagramChannel

        SocketChannel

        ServerSocketChannel

FileChannel主要用于文件传输,其他三种用于网络通信。

Buffer(缓冲区)

缓冲区;实际上是一个容器对象,对数组进行了封装,用数组来缓存数据,还定义了一些操作数组的API,如 put()、get()、flip()、compact()、mark() 等。在NIO中,无论读还是写,数据都必须经过Buffer缓冲区.

        ByteBuffer

                MappedByteBuffer
                DirectByteBuffer
                HeapByteBuffer

        ShortBuffer
        IntBuffer
        LongBuffer
        FloatBuffer
        DoubleBuffer
        CharBuffer
其中最常用的是ByteBuffer

Selector(选择器)

        选择器;是一个特殊的组件,用于采集各个通道的状态(或者事件)

socket连接方法

Socket编程理解为对TCP协议的具体实现。

多线程技术

系统为每一个连接分配一个thread(线程),分别去处理对应的socket连接

缺点:

        1.内存占用高。每有一个socket连接,系统就要分配一个线程去对接。当出现大量连接时,会开辟大量线程,导致占用大量内存。

        2.线程上下文切换成本高

        3.只适合连接数较少的场景

线程上下文切换:
        一个CPU在同一个时刻是只能处理一个线程的,由于时间片耗尽或出现阻塞等情况,CPU 会转去执行另外一个线程,这个叫做线程上下文切换

线程池技术

使用线程池,让线程池中的线程去处理连接

缺点:

        1.在阻塞模式下,线程只能处理一个连接。线程池中的线程获取任务,只有当任务完成/socket断开连接,才会去获取执行下一个任务

        2.只适合短链接的场景

selector技术

为每个线程配合一个选择器,让选择器去管理多个channel。(注:FileChannel是阻塞式的,因此无法使用选择器。)
让选择器去管理多个工作在非阻塞式下的Channel,获取Channel上的事件,当一个Channel没有任务时,就转而去执行别的Channel上的任务。这种适合用在连接多,流量小的场景。

        若事件未就绪,调用 selector 的 select() 方法会阻塞线程,直到 channel 发生了就绪事件。这些事件就绪后,select 方法就会返回这些事件交给 thread 来处理。

ByteBuffer

简单示例

public class TestByteBuffer {
    public static void main(String[] arge){

        try{
            //1.输入输出流,文件数据传输
            FileChannel channel = new FileInputStream("network-program/data.txt").getChannel();

            //2.准备缓冲区,并设置大小
            ByteBuffer buffer = ByteBuffer.allocate(10);

            //3.从channel读取数据,并写入buffer中
            channel.read(buffer);

            //4.buffer切换成读模式
            buffer.flip();

            //5.判断是否还有剩余未读数据
            while (buffer.hasRemaining()){
                byte b = buffer.get();

                System.out.print((char)b);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

使用步骤

1.向buffer写入数据,如:channel.read(buffer);

2.调用flip()切换至读模式

3.从buffer读取数据,如:buffer.get();

4.调用clear()或compact()切换到写模式

属性

        capacity:缓冲区的容量,不可变

        limit:缓冲区的界限。limit之后的数据不允许读写

        position:读写指针。position不可大于limit,且position不为负数

        mark:标记。记录当前position的值。position被改变后,可以通过调用reset() 方法恢复到mark的位置

 常见方法

allocate方法

        通过allocate我们可以给ByteBuffer分配空间,但是这个空间不可以动态变换,如果想要改变ByteBuffer的大小只能重新分配一个

ByteBuffer.allocate(10);

allocateDirect方法

通过allocateDirect我们也可以给ByteBuffer分配空间

ByteBuffer.allocateDirect(10);

allocate 与 allocateDirect的区别:

1.allocate创建出来的是HeapByteBuffer对象,allocateDirect创建出来的是DirectByteBuffer对象

2.HeapByteBuffer是存在于JVM的堆内存中,DirectByteBuffer是存在于直接(系统)内存中

3.HeapByteBuffer的读写效率低于DirectByteBuffer,因为HeapByteBuffer存在于jvm中的,自然会收到垃圾回收器的影响

4.DirectByteBuffer使用不当,容易造成内存泄露

put方法

put方法可以将数据放入到缓冲区中。操作完成后,position的值会+1,并指向下一个可存放的区域,limit=capacity

buffer.put(byte b);

flip方法

flip方法会切换对当前缓冲区的去操作,写/读->读/写

buffer.flip();

当是读模式切换到写模式时,恢复为put时的值。 

get方法

get方法会读取缓冲区里的数据,一次只能读取一个。读取后,position的值会+1,指向下一个可读区。当position大于limit时,会报异常。get方法如果传入指定的索引位置:get(i)。则position的值不会产生变动。

buffer.get();

clear方法

clean方法就像初始化一样,会把ByteBuffer的里属性值都恢复到最初,并且清除缓冲区里的数据。

buffer.clear();

compact方法

compact方法会把已经读取的数据清除,后面未读取的数据向前压缩,然后切换到写模式。
数据前移后,原始位置的数据不会清楚,但是在后面的写入操作中会被覆盖。

buffer.compact();

rewind方法

rewind方法只能在读模式下使用,使用后,会恢复position、limit和capacity的值

buffer.rewind();

mark方法和reset方法

这个两个方法通常都是搭配着使用。
mark做一个标记,会保存当前position的值;reset方法会把mark保存的值重新赋给position。

buffer.mark();

buffer.reset();

字符串与ByteBuffer的相互转换

方法一:

        // 编码:字符串的getByte方法
        ByteBuffer buffer = ByteBuffer.allocate(15);
        buffer.put(str.getBytes());

方法二:

        // 编码:StandardCharsets的encode方法获取ByteBuffer
        ByteBuffer buffer2 = StandardCharsets.UTF_8.encode(str);

方法三:

        ByteBuffer buffer3 = ByteBuffer.wrap(str.getBytes());
        // 解码: 通过StandardCharsets的decoder方法解码
        String decodeStr3 = StandardCharsets.UTF_8.decode(buffer3).toString();

黏包和半包

黏包:发送方在发送数据时,并不是一条一条地发送数据,而是将数据整合在一起,当数据达到一定的数量后再一起发送。这就会导致多条信息被放在一个缓冲区中被一起发送出去。
半包:因为我们分配缓冲区的大小是固定,如果空间小于数据量,那就只能先把当前缓冲区里的数据读取完,再去接收剩下的的数据。数据就会出现被截断的断层现象。

如:

  • Hello world!\n

  • I’m LIKEGAKKI!\n

  • How are you?\n
    经过传输后,服务端的产生了两个ByteBuffer:

  • Hello,world\nI’m LIKEGAKKI\nHo(黏包)

  • w are you?\n?(半包)

重新拆分:

public class TestByteBufferExam {
    public static void main(String[] args){
        ByteBuffer buffer = ByteBuffer.allocate(32);

        buffer.put("Hello,world\nI,m zhangsan\nHo".getBytes());
        split(buffer);
        buffer.put("w are you?\n".getBytes());
        split(buffer);

    }

    private static void split(ByteBuffer buffer){
        buffer.flip();

        for(int i = 0;i<buffer.limit();i++){
            if(buffer.get(i) == '\n'){
                int length = i + 1 - buffer.position();

                ByteBuffer byteBuffer = ByteBuffer.allocate(length);

                for(int j = 0;j<length;j++){
                    byteBuffer.put(buffer.get());
                }

                System.out.println(byteBuffer.get());
            }
        }

        buffer.compact();
    }

在循环中用get(i)方法依次读取数据,当读取的数据匹配‘\n’时,说明之前的读取的是一段信息。

记录该段数据长度,以便于申请对应大小的缓冲区;将缓冲区的数据通过get()方法写入到target中。

调用compact方法切换模式,因为缓冲区中可能还有未读的数据。

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

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

相关文章

Php_Code_challenge12

题目&#xff1a; 答案&#xff1a; 解析&#xff1a; 字符串拼接。

城市内涝模拟:慧天【HTWATER】软件,完全兼容SWMM模型格式,可以在本平台模型与SWMM模型之间实现转换

在城市排水防涝规划过程中&#xff0c;水文水动力耦合模型已经成为一种不可或缺的分析工具。在模型建立、城市内涝风险评估、排水系统性能诊断以及海绵城市规划等方面&#xff0c;内涝耦合模型提供了相应的模拟及分析工具&#xff1a; 一、丰富的数据处理功能&#xff0c;兼容…

算法刷题笔记(3.25-3.29)

算法刷题笔记 3.25-3.29 1. 相同的树2. 二叉树的最近公共祖先3. 二叉搜索树中第K小的元素通过双端队列duque 中序遍历 4. 二叉树的锯齿形层序遍历new LinkedList<Integer>(levelList)双端队列复制 数组需要左右顺序&#xff0c;考虑双端队列 5. 岛屿数量6. 字典序排数&am…

计算机视觉的应用25-关于Deeplab系列语义分割模型的应用场景,以及空洞卷积的介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用25-关于Deeplab系列语义分割模型的应用场景&#xff0c;以及空洞卷积的介绍。Deeplab是Google研发的一系列深度学习模型&#xff0c;主要用于图像语义分割任务&#xff0c;其在众多应用场景中展现出…

基于8086密码锁可修改仿真

**单片机设计介绍&#xff0c;基于8086密码锁可修改仿真 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于8086的密码锁可修改仿真设计是一个结合了微处理器控制、密码管理和仿真技术的综合性项目。通过此设计&#xff0c;用户可以设定和…

nginx界面管理工具之nginxWebUI 搭建与使用

nginx界面管理工具之nginxWebUI 搭建与使用 一、nginxWebUI 1.nginx网页配置工具 官网地址: http://www.nginxwebui.cn 源码地址&#xff1a;https://git.chihiro.org.cn/chihiro/nginxWebUI 2.功能说明 本项目可以使用WebUI配置nginx的各项功能, 包括http协议转发, tcp协议…

非关系型数据库--------------Redis配置与优化

一、关系数据库与非关系型数据库 1.1关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上一般面向于记录。SQL语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#xff0c;用…

正弦实时数据库(SinRTDB)的使用(10)-数据文件的无损压缩

前文已经将正弦实时数据库的使用进行了介绍&#xff0c;需要了解的可以先看下面的博客&#xff1a; 正弦实时数据库(SinRTDB)的安装 正弦实时数据库(SinRTDB)的使用(1)-使用数据发生器写入数据 正弦实时数据库(SinRTDB)的使用(2)-接入OPC DA的数据 正弦实时数据库(SinRTDB)…

Python 用pygame简简单单实现一个打砖块

# -*- coding: utf-8 -*- # # # Copyright (C) 2024 , Inc. All Rights Reserved # # # Time : 2024/3/30 14:34 # Author : 赫凯 # Email : hekaiiii163.com # File : ballgame.py # Software: PyCharm import math import randomimport pygame import sys#…

JAVA学习笔记21

1.IDEA的使用 1.ctrl B 快速定位到方法 2.ctrl Y 快速删除行 3.ctrl D 快速复制行 4.ctrl H 查看继承的层级关系 5.快速格式化代码 ctrl shift L 6.alt R 快速允许程序 7.ctrl / 快速添加注释 1.包(软件包) 1.1包的三大作用 1.区分相同名字的类 2.当类很多的…

企业数智化进入“加速期”,互联网企业越来越离不开IDC机房

近年来&#xff0c;随着人工智能技术的快速发展和大数据的广泛应用&#xff0c;企业数智化已经进入了“加速期”。越来越多的企业意识到&#xff0c;通过数字化技术和数据分析&#xff0c;可以提高企业的运营效率、降低成本、改善决策能力&#xff0c;进而在激烈的市场竞争中取…

奥比中光Astra SDK相机SDK openni相机成像原理

目录 1.1 成像原理简介 1.1.1 结构光 1.1.2 双目视觉 1.1.3 光飞行时间TOF​ 2.使用手册 参考网址 2.1 产品集成设计 2.2 SDK介绍与使用 2.3 常用API介绍 OPENNI API 2 OpenNI类&#xff08;OpenNI.h&#xff09; 1.1 成像原理简介 1.1.1 结构光 结构光&#xff0…

关于POE供电你必须知道的13个问题

你们好&#xff0c;我的网工朋友。 近年来&#xff0c;PoE供电技术的发展势头越来越强劲。 凭借简化用电设备的安装和部署、节能&#xff0c;安全等一系列优势&#xff0c;PoE供电成为无线覆盖、安防监控、以及智能电网等场景的新宠。 在技术交流中&#xff0c;工程商困惑最…

Dijkstra堆优化之蓝桥王国

Dijkstra堆优化 Dijkstra算法是一种用于解决单源最短路径问题的算法&#xff0c;即从图中的一个顶点出发到所有其他顶点的最短路径。然而&#xff0c;处理大图时&#xff0c;常规的Dijkstra算法可能会遇到性能问题。这就是Dijkstra的堆优化算法派上用场的地方。在堆优化版本中…

【小黑送书—第十八期】>>让工作自动化起来!无所不能的Python(文末送书)

随着我国企业数字化和信息化的深入&#xff0c;企业对办公自动化的效率和灵活性要求越来越高。Python作为一种开源的软件应用开发方式&#xff0c;通过提供强大丰富的库文件包&#xff0c;极大地简化了应用开发过程&#xff0c;降低了技术门槛。Python开发有哪些优势、挑战以及…

如何查找合适自己的EI期刊和会议?

大家都知道EI工程索引包含期刊和会议&#xff0c;两者含金量都是比较高的&#xff0c;那么如何才能找到适合自己的EI期刊和会议?ei期刊数量众多&#xff0c;ei国际会议举办次数也是很多的&#xff0c;下面分享几种查找的渠道仅供参考&#xff1a; 渠道一、通过搜索引擎查找&am…

【蓝桥杯第十三届省赛B组】(详解)

九进制转十进制 #include <iostream> #include<math.h> using namespace std; int main() {cout << 2*pow(9,3)0*pow(9,2)2*pow(9,1)2*pow(9,0) << endl;return 0; }顺子日期 #include <iostream> using namespace std; int main() {// 请在此…

【Emgu CV教程】10.11、MatchShapes()比较轮廓的相似度

文章目录 一、函数介绍二、演示1.原始素材2.代码3.运行结果 一、函数介绍 MatchShapes()函数&#xff0c;可以比较两个轮廓的相似度&#xff0c;而且对于旋转、放大、缩小的轮廓都能适用&#xff0c;利用这个函数就能实现最简单的物体检测。函数官方定义如下 public static d…

【机器学习】代价函数

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

Web应急响应

2024年护网将至&#xff0c;最近我将分享一些红蓝对抗的一些技巧&#xff0c;应急响应、信息收集相关的知识概念以及相关技巧。 目录 1. 黑客攻击流程 2. webshell流量特征 1.1.菜刀特征 1.2.冰蝎3.0 &#xff1a; 1.3.冰蝎2.0&#xff1a; 1.4.冰蝎3.11流量特征 1.5.蚁…