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.mzph.cn/news/785163.shtml

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

相关文章

mongodb sharding分片模式的集群数据库,日志治理缺失导致写入数据库报错MongoWriteConcernException的问题总结(下)

一、接着上文 上文介绍了mongodb sharding的分片集群搭建&#xff0c;本文侧重于讲述日志治理。 这里使用linux自带的日志治理工具logrotate&#xff0c;无论是哪个端口的进程&#xff0c;其日志治理方式类似。 查看/data目录下的文件大小&#xff0c; du -hs *二、Logrota…

每日一题(相交链表 )

欢迎大家来我们主页进行指导 LaNzikinh-CSDN博客 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节…

RUST使用crates.io上的依赖完整教程

1.打开crates.io 2.搜索要使用的依赖,如rand 点击包名,进入包详情页面: 添加依赖方法有两种 1.使用cargo命令 2.直接修改Cargo.toml 使用cargo命令操作如下: 在工程目录执行如下命令: cargo add rand 执行完成后如自动向Cargo.toml中添加依赖如下: 手动修改Cargo.toml是…

漏洞挖掘 | ruoyi框架管理系统漏洞

前言&#xff1a; 在挖src的时候&#xff0c;可以通过信息收集收集弱口令&#xff0c;然后通过后台弱口令进入后台&#xff1a; 发现一个弱口令进去后&#xff1a; 【魔方老师提醒才发现&#xff0c;这个蓝色的草丛其实可以大致判断是若依系统】 看这界面&#xff0c;是不是…

XSS伪协议

XSS伪协议简介 XSS&#xff08;跨站脚本攻击&#xff09;中的伪协议是指利用一些浏览器允许的特殊协议来执行恶意脚本的一种方式。常见的伪协议包括 javascript:, data:, vbscript: 等。 攻击者可以通过构造特定的URL&#xff0c;将恶意脚本注入到网页中&#xff0c;从而实现…

基于8086密码锁可修改仿真

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

海外媒体宣发技巧解析从而提升宣发效果

在当今全球化的媒体环境下&#xff0c;海外媒体宣发是企业和品牌推广的重要手段。然而&#xff0c;要在海外市场取得成功&#xff0c;一味地复制国内的宣发策略是行不通的。要想提升宣发效果&#xff0c;就必须了解并掌握一些海外媒体宣发的技巧。世媒讯一家从事海内外媒体的推…

git配置SSH 密钥

git配置SSH 密钥 1.window配置ssh1.安装ssh2.安装 Git&#xff08;安装教程参见安装Git&#xff09;并保证版本大于 1.9![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e59f4e16b83c45649f1d9d7bd6bf92c0.png)3.SSH 尽量保持最新&#xff0c;6.5之前的版本由于使用…

用ChatGPT出题,完全做不完

最近小朋友正在学习加减法&#xff0c;正好利用ChatGPT来生成加减法练习题&#xff0c;小朋友表示够了&#xff0c;够了&#xff0c;完全做不完。本文将给大家介绍如何利用ChatGPT来生成练习题。 尚未获得ChatGPT的用户&#xff0c;请移步&#xff1a;五分钟开通GPT4.0。 角色…

Cadence HDL原理图创建时多个VCC或GND处理方法

1.先new一个 2. 下面的Global pin 的name处不要直接使用GND&#xff0c;不然后期画图容易混淆。 数字地使用VSS&#xff1b;模拟地使用VEE等 3. 之后继续按照普通原理图进行绘制即可。 原理图封装绘制 4. 最后在原理图中要对该网络进行说明&#xff01;&#xff01;&#…

判断点在多边形内的算法

在计算几何中&#xff0c;判定点是否在多边形内&#xff0c;是个非常有趣的问题。通常有两种方法&#xff1a; 一、Crossing Number&#xff08;交叉数&#xff09; 它计算从点P开始的射线穿过多边形边界的次数。当“交叉数”是偶数时&#xff0c;点在外面;当它是奇数时&…

【蓝桥杯第十三届省赛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() {// 请在此…

为什么很多程序员都建议使用 Linux?

一个好的操作系统应该是有什么问题普通用户搞不定&#xff0c;但程序员肯定搞得定。Linux就是这样的操作系统。 Windows嘛&#xff0c;出问题了普通用户搞不定&#xff0c;程序员也搞不定&#xff0c;某些运维能搞定&#xff0c;某些只有m$才搞定。在windows面前程序员都得向m…

程序员35岁会失业吗?会!!!!

程序员35岁会失业吗&#xff1f; 35岁被认为是程序员职业生涯的分水岭&#xff0c;许多程序员开始担忧自己的职业发展是否会受到年龄的限制。有人担心随着年龄的增长&#xff0c;技术更新换代的速度会使得资深程序员难以跟上&#xff1b;而另一些人则认为&#xff0c;丰富的经…

【二叉树】Leetcode 114. 二叉树展开为链表【中等】

二叉树展开为链表 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…

dbeaver连接 MySQL 报错处理

场景&#xff1a; 点击连接 MySQL 的时候出现 Public Key Retrieval is not allowed 解决 编辑连接-》驱动属性设置&#xff0c;设置 true 点击测试连接如下&#xff1a; 原因分析&#xff1a; 在 MySQL 中&#xff0c;“Public Key Retrieval” 参数通常与 SSL 连接有关…

物联网网关和飞鸟物联平台如何助力其实现智能化升级,提升生产效率

随着工业4.0时代的到来&#xff0c;物联网技术逐渐成为推动工业转型升级的关键力量。物联网网关作为连接工业设备与网络的核心枢纽&#xff0c;在工业自动化、数据收集与分析等方面发挥着越来越重要的作用。本案例将围绕一家知名制造企业&#xff0c;展示物联网网关和飞鸟物联平…

STM32/GD32的以太网DMA描述符

继续梳理以太网的DMA描述符。 以太网DAM描述符的结构 有两种结构&#xff0c;链式结构和环形结构。 常用的是链式结构。 标准库中&#xff0c;关于DMA描述符的数据结构 以gd32f4xx_enet.c为例。 先说发送描述符。 系统分配了5个发送描述符。每个描述符对应的缓冲区大小为152…

四川古力未来科技抖音小店:把握电商新风口,前景无限广阔

在数字化浪潮席卷全球的今天&#xff0c;电商行业以其独特的魅力和无限潜力&#xff0c;成为了众多创业者和投资者关注的焦点。四川古力未来科技抖音小店&#xff0c;正是站在这一风口浪尖上的新兴力量&#xff0c;其前景之广阔&#xff0c;令人瞩目。 抖音&#xff0c;作为一款…