Netty教程之NIO基础

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方法获取ByteBufferByteBuffer 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.rhkb.cn/news/293492.html

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

相关文章

ctf题目

目录 1.文件包含的一道题目&#xff0c;没什么难度&#xff0c; 2.一道sql注入的题目&#xff0c;伪静态 3.限制只能本地访问。 1.文件包含的一道题目&#xff0c;没什么难度&#xff0c; 但是一个点就是它这里去包含的那个文件名就是flag&#xff0c;而不是flag.php也不是f…

CSS实现小车旅行动画实现

小车旅行动画实现 效果展示 CSS 知识点 灵活使用 background 属性下的 repeating-linear-gradient 实现路面效果灵活运用 animation 属性与 transform 实现小车和其他元素的动画效果 动画场景分析 从效果图可以看出需要实现此动画的话&#xff0c;需要position属性控制元素…

学习transformer模型-Dropout的简明介绍

Dropout的定义和目的&#xff1a; Dropout 是一种神经网络正则化技术&#xff0c;它在训练时以指定的概率丢弃一个单元&#xff08;以及连接&#xff09;p。 这个想法是为了防止神经网络变得过于依赖特定连接的共同适应&#xff0c;因为这可能是过度拟合的症状。直观上&#…

移动硬盘无法打开?原因与解决方案一网打尽

一、遭遇困境&#xff1a;移动硬盘突然罢工 在数字化日益盛行的今天&#xff0c;移动硬盘无疑是我们储存和转移数据的重要工具。然而&#xff0c;当某一天你突然发现移动硬盘无法打开时&#xff0c;那种焦虑与无助感无疑会席卷而来。插上电脑&#xff0c;硬盘灯闪烁却无响应&a…

软考之零碎片段记录(二)

一、位示图记录磁盘使用情况 1. 计算位示图的大小&#xff1f; 物理块大小4MB, 磁盘容量512GB, 系统字长64位&#xff08;64位 / 字长&#xff09; 计算物理块数量 512 * 1024 / 4 256 * 512 131072 每一位记录一个物理块 131072 / 64 2048&#xff08;字&#xff09; 二…

科研学习|论文解读——情感对感知偶然信息遭遇的影响研究(JASIST,2022)

原文题目 Investigating the impact of emotions on perceiving serendipitous information encountering 一、引言 serendipity一词最初是由霍勒斯沃波尔创造的&#xff0c;他将其定义为“通过意外和睿智发现你并不追求的事物”。信息研究中大多数现有的偶然性定义从几个角度看…

vue2 el-table指定某些数据不参与排序

vue2 el-table指定某些数据不参与排序 1、需求描述2、配置属性方法3、详细代码如下 1、需求描述 最后一行总计不参与排序 2、配置属性方法 el-table 需要配置 sort-change"soltHandle" 方法 el-table-column 需要配置 sortable"custom"属性3、详细代码如…

单链表的插入和删除

一、插入操作 按位序插入&#xff08;带头结点&#xff09;&#xff1a; ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 typedef struct LNode{ElemType data;struct LNode *next; }LNode,*LinkList;//在第i 个位置插插入元素e (带头结点) bool Li…

Bridge Champ与Ignis公链:探索Web3游戏的新未来

在数字化和去中心化的浪潮中&#xff0c;Web3游戏与公链的融合为游戏行业带来了新的变革。特别是&#xff0c;Bridge Champ和Ignis公链的结合&#xff0c;展示了一种全新的游戏生态模式&#xff0c;不仅为玩家提供了更加公平、透明的游戏体验&#xff0c;同时也为游戏开发和运营…

Day50:WEB攻防-PHP应用文件包含LFIRFI伪协议编码算法无文件利用黑白盒

目录 文件包含-原理&分类&利用&修复 文件读取 文件写入 代码执行 远程利用思路 黑盒利用-VULWEB 白盒利用-CTFSHOW-伪协议玩法 78-php&http协议 79-data&http协议 80-81-日志包含 87-php://filter/write&加密编码 88-data&base64协议 …

磐启/PAN7030/2.4GHz 无线收发SOC芯片/ESSOP10/SOP16

1 概述 PAN7030 是一款集成 8 位 OTP MCU 和 2.4GHz 无线收发电路芯片&#xff0c;适合应用于玩具小车、 遥控器等领域。 PAN7030 内置 8 位 OTP MCU&#xff0c;包括 1.25KW 的程序存储器、80 字节数据存储器、16 位定 时器和 8 位/11 位 PWM 定时器、看门狗、电压比较器等…

Ubuntu18.04 下Ublox F9P 实现RTK (利用CORS服务无需自建基站)

本内容参考如下连接:Ubuntu下Ublox F9P利用CORS服务无需自建基站实现RTK-CSDN博客 一、Ublox F9P 硬件模块示意图 图中展示了Ublox F9P的接口,包括串口2(`UART1`和`UART2`),USB1。需要人为通过u-center(Ublox F9P的显示软件)软件设置以下功能: Ublox通过`UART1`向PC端发送…

Go的数据结构与实现【Binary Search Tree】

介绍 本文用Go将实现二叉搜索树数据结构&#xff0c;以及常见的一些方法 二叉树 二叉树是一种递归数据结构&#xff0c;其中每个节点最多可以有两个子节点。 二叉树的一种常见类型是二叉搜索树&#xff0c;其中每个节点的值都大于或等于左子树中的节点值&#xff0c;并且小…

脑机辅助推导算法

目录 一&#xff0c;背景 二&#xff0c;华容道中道 1&#xff0c;问题 2&#xff0c;告诉脑机如何编码一个正方形格子 3&#xff0c;让脑机汇总信息 4&#xff0c;观察图&#xff0c;得到启发式算法 5&#xff0c;根据启发式算法求出具体解 6&#xff0c;可视化 一&am…

centos7.5安装gitlab-runner,配置CI/CD流水线

一般不建议gitlab-server和gitlab-runner装在同一台服务器 第一步&#xff1a;安装gitlab-runner,最好和gitlab实例版本一致 # 下载官方gitlab-runner安装脚本 curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | s…

时序预测 | Python实现VMD-CNN-LSTM时间序列预测

时序预测 | Python实现VMD-CNN-LSTM时间序列预测 目录 时序预测 | Python实现VMD-CNN-LSTM时间序列预测预测效果基本介绍模型描述代码设计预测效果 基本介绍 VMD-CNN-LSTM 是一种混合深度学习模型,结合了变分模态分解(VMD)、卷积神经网络(CNN)和长短期记忆网络(LSTM)的…

【spring】@Autowired注解学习

Autowired介绍 Spring框架是Java领域中一个非常重要的企业级应用开发框架&#xff0c;它提供了全面的编程和配置模型&#xff0c;旨在帮助开发者更快速、更简单地创建应用程序。在Spring框架中&#xff0c;Autowired是一个非常重要的注解&#xff0c;它用于实现依赖注入&#…

Chatopera 云服务的智能问答引擎实现原理,如何融合 #聊天机器人 技术 #Chatbot #AI #NLP

观看视频 Bilibili: https://www.bilibili.com/video/BV1pZ421q7EH/YouTube: https://www.youtube.com/watch?vx0d1_0HQa8o 内容大纲 提前在浏览器打开网址&#xff1a; Chatopera 云服务&#xff1a;https://bot.chatopera.comChatopera 入门教程&#xff1a;https://dwz…

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.蚁…

LeetCode-56. 合并区间【数组 排序】

LeetCode-56. 合并区间【数组 排序】 题目描述&#xff1a;解题思路一&#xff1a;排序&#xff1f;怎么排&#xff1f;当然是排各个区间的左边界&#xff0c;然后判断下一个边界的左边界与结果数组里面的右边界是否重叠。解题思路二&#xff1a;优化解题思路三&#xff1a;0 题…