Netty: NIO网络编程

news2024/11/23 4:43:55

文章目录

  • 一、NIO介绍
  • 二、NIO原理
  • 三、Buffer
    • 1、Buffer原理介绍
    • 2、Buffer实现类
    • 3、示例
    • 4、NIO和BIO的比较
  • 四、Channel
    • 1、介绍
    • 2、FileChannel介绍
    • 3、Buffer和Channel的注意事项
  • 五、Selector
  • 六、Selector、Channel和Buffer关系

一、NIO介绍

NIO介绍

二、NIO原理

  • NIO有三大核心部分: 通道(Channel)、缓冲区(Buffer)和选择器(Selector)。Channel是对原I/O体系中流的模拟,可以通过Channel完成数据的读/写操作;Buffer则用于存储数据,它是NIO与普通I/O的主要区别之一;而Selector则用于监听多个Channel的状态,以实现多路复用。

  • 在NIO的工作过程中,数据首先从Channel读取到Buffer中,或者从Buffer写入到Channel中。这个过程是异步的,因此不会阻塞线程。同时,Selector会不断地轮询注册的Channel,查看是否有已经就绪的I/O操作(例如读或写),如果有,就通知相应的线程进行处理。

  • Java NIO的非阻塞模式,是一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以知道数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

  • 通俗理解:NIO是可以做到用一个线程来处理多个操作的。假设有10000个请求过来,根据实际情况,可以分配50或100个线程来处理。不像之前的阻塞 IO那样,非得分配10000个线程。

  • HTT2.0使用了多路复用的技术,做到同一个链接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。

总的来说,NIO通过非阻塞I/O操作和事件驱动机制,提高了系统的并发性能和响应速度,使得处理大量连接和I/O操作变得更加高效。因此,NIO已经被越来越多地应用到大型应用服务器中,成为解决高并发与大量连接、I/O处理问题的有效方式。

三、Buffer

1、Buffer原理介绍

Java NIO的Buffer类是一个用于特定基本数据类型的容器。它是一个对象,包含一些要写入或者要读出的数据。在NIO中,所有的数据都是用Buffer处理的。在数据读写之前,需要先放入Buffer,或者从Buffer中取出。Buffer对象中提供了一组方法,可以轻松的使用内存块,此外缓冲区对象那中内置了一些机制,能够追踪和记录缓冲区的状态变化情况。

以下是Java NIO Buffer的基本使用步骤:

  • 分配Buffer:首先,你需要通过调用allocate()方法(对于ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer等)在JVM的堆中分配一个新的Buffer。例如,以下代码分配一个新的ByteBuffer:
ByteBuffer buffer = ByteBuffer.allocate(48);
  • 写入数据到Buffer:调用Buffer类的put()方法将数据写入Buffer。例如:
buffer.put((byte) 10);  
buffer.put((byte) 20);
  • 切换Buffer的读写模式:在写数据到Buffer后,需要调用flip()方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。换句话说,limit现在表示的是之前写进Buffer的元素的个数。现在Buffer准备好从写模式切换到读模式:
buffer.flip();
  • 从Buffer中读取数据:从Buffer中读取数据,你可以使用get()方法。读取数据直到hasRemaining()返回false。这表示已经到达Buffer的末尾:
while(buffer.hasRemaining()){  
    byte b = buffer.get();  
    // do something with b  
}
  • 清除Buffer:当读完Buffer中的数据后,需要清除它以便下次使用。有两种方式可以清除Buffer:调用clear()或compact()方法。clear()方法会清除整个Buffer。compact()方法只会清除已经读过的数据。任何未读的数据都被移到Buffer的起始处,新写入的数据将放到Buffer的未读数据后面。
buffer.clear(); // 清除整个Buffer

或者

buffer.compact(); // 只清除已读数据

注意,在使用Buffer时,要时刻注意Buffer的容量(capacity),位置(position)和限制(limit)。容量是Buffer能够容纳的数据元素的最大数量;位置是下一个要被读或写的元素的索引;限制是第一个不应该被读或写的元素的索引。这些值可以通过调用Buffer的相应方法来获取或设置。

以上就是Java NIO Buffer的基本使用方法。通过合理地使用Buffer,你可以有效地处理大量的数据读写操作,提高程序的性能。

2、Buffer实现类

  • 在NIO中,Buffer是一个顶层父类,它是一个抽象类,类的层级关系下图:
    在这里插入图片描述

    • ByteBuffer:存储字节数据到缓冲区。
      在这里插入图片描述

    • ShortBuffer:存储字符串数据到缓冲区。

    • CharBuffer:存储字符数据到缓冲区。

    • IntBuffer:存储整数数据到缓冲区。

    • LongBuffer:存储长整型数据到缓冲区。

    • DoubleBuffer:存储小数到缓冲区。

    • FloatBuffer:存储小数到缓冲区。

  • Buffer类定义了所有缓冲区都具有的四个属性,来提供其所包含的数据元素的信息:
    | 属性 |描述 |
    |–|–|
    |capacity | 容量,即可以容纳的最大数据量;在缓冲区创建是被设定并且不能改变 |
    |Limit| 表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行操作。且极限是可以修改的|
    |position| 位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变,为下次读写做准备|
    |mark| 标记|

  • Buffer类相关的方法:

//JDK1.4,引入的api
public final int capacity(); 	//返回此缓冲区的容量
public final int position();	//返回此缓冲区的位置
public final Buffer position(int newPosition);	//设置此缓冲区的位置
public final int limit();	//返回此缓冲区的限制
public final Buffer limit(int newLimit);	//设置此缓冲区的限制
public final Buffer mark();		//在此缓冲区的位置设置标记
public final Buffer reset();	//将此缓冲区的位置重置为以前标记的位置
public final clear();	//清除此缓冲区,即将各个标记恢复到初始化状态,但是数据并没有真正擦除。
public final Buffer flip();		//反转此缓冲区
public final Buffer rewind();	//重绕此缓冲区
public final int remaining();	//返回当前位置与限制之间的元素数
public final boolean hasRemaining();	//告知当前位置和限制之间是否有元素
public final boolean isReadOnly();		//告知此缓冲区是否为只读缓冲区

//JDK1.6 引入的api
public abstract boolean hasArray();		//告知此缓冲区是否具有访问的底层实现数组
public abstract Object array();			//返回此缓冲区的底层实现数组
public abstract int arrayOffset();		//返回此缓冲区的底层实现数组中第一个缓冲区元素的偏移量
public abastract boolean isDirect();	//告知此缓冲区是否为直接缓冲区

3、示例

package test.nio;

import java.nio.IntBuffer;
public class BasicBuffer {
    public static void main(String[] args) {
        //创建一个buffer, 容量大小为5,即可以存放5个int
        IntBuffer buffer = IntBuffer.allocate(5);

        //向buffer中存放数据
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put(i * 2);
        }

        //从buffer中读取数据
        buffer.flip();
        while(buffer.hasRemaining()) {
            int i = buffer.get();
            System.out.println(i);
        }
    }
}

运行结果:

0
2
4
6
8

Process finished with exit code 0

4、NIO和BIO的比较

  • BIO以流的方式处理数据,而NIO以块的方式处理数据,块I/O的效率比流I/O高很多。
  • BIO是阻塞的,NIO则是非阻塞的。
  • BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓存区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。

四、Channel

1、介绍

  • Channel提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer。
    在这里插入图片描述

  • NIO的通道类似于流,有如下区别:

    • 通道可以同时进行读写,而流只能读或者只能写。
    • 通道可以实现异步读写数据。
    • 通道可以从缓冲读数据,也可以写数据到缓冲:
      在这里插入图片描述
    • BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据的操作。
  • BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据的操作,而NIO中的通道(Channel)是双向的,可以读操作,也可以写操作。

  • Channel在NIO中是一个接口

public interface Channel extends Closeable
  • 常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel和SocketChannel(ServerSocketChannel类似ServerSocket、SocketChannel类似Socket)。
  • FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel

2、FileChannel介绍

  • FileChannel主要用来对本地文件进行IO操作,常见的方法有:

    • public int read(ByteBuffer dst),从通道读取数据并放到缓冲区中。
    • public int write(ByteBuffer src),把缓冲区的数据写到通道中。
    • public long transferForm(ReadableByteChannel src, long position, long count),从目标通道中复制数据到当前通道。
    • public long transferTo(long position, long count, WritableByteChannel target),把数据从当前通道复制给目标通道。
  • 通过channel向文件中写数据。
    blog.csdnimg.cn/direct/a4b5dc74278c40d88a1f791ab936b1c9.png)

package test.nio;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileChannel {
    public static void main(String[] args) {
        String str = "hello world 今天是星期日";
        try {
            //创建一个输出流->channel
            FileOutputStream fileOutputStream = new FileOutputStream("d:\\file01.txt");

            //通过fileOutputStream获取对应的FileChannel
            //这个fileChannel真实类型是FileChannelImpl
            FileChannel channel = fileOutputStream.getChannel();

            //创建一个缓冲区ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            //将str放入byteBuffer
            byteBuffer.put(str.getBytes());

            //对byteBuffer进行flip
            byteBuffer.flip();

            //将byteBuffer,数据写入到fileChannel
            channel.write(byteBuffer);
            fileOutputStream.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

  • 通过Channel读取本地文件数据。
    在这里插入图片描述
package test.nio;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel01 {
    public static void main(String[] args) throws IOException {
        File file = new File("d:\\file01.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        FileChannel channel = fileInputStream.getChannel();

        //创建一个缓冲区ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate((int)file.length());

        //将通道的数据读入到Buffer
        int read = channel.read(byteBuffer);

        //将byteBuffer的字节数据转成String
        byteBuffer.flip();
        String str = new String(byteBuffer.array());
        System.out.println(str);
        fileInputStream.close();
    }
}

结果展示:

hello world 今天是星期日

Process finished with exit code 0
  • 使用一个Buffer完成文件的读取
package test.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOFileChannel02 {
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream("d:\\file01.txt");
        FileChannel channel = fileInputStream.getChannel();

        FileOutputStream fileOutputStream = new FileOutputStream("d:\\file02.txt");
        FileChannel channel1 = fileOutputStream.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        while (Boolean.TRUE) {
            byteBuffer.clear();
            int read = channel.read(byteBuffer);
            System.out.println("read = " + read);
            if (read == -1) {
                break;
            }

            byteBuffer.flip();
            channel1.write(byteBuffer);
        }
        fileInputStream.close();
        fileOutputStream.close();
    }
}

  • 拷贝文件transferFrom方法
    要求:使用FileChannel(通道)和方法transferFrom,完成文件的拷贝。
package test.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class NIOFileChannel03 {
    public static void main(String[] args) throws IOException {
        //创建相关的流
        FileInputStream fileInputStream = new FileInputStream("d:\\star.png");
        FileOutputStream fileOutputStream = new FileOutputStream("d:\\ab.png");

        //获取各个流对应的fileChannel
        FileChannel channel = fileInputStream.getChannel();
        FileChannel channel1 = fileOutputStream.getChannel();

        //使用transform完成拷贝
        channel1.transferFrom(channel, 0, channel.size());

        //关闭相关通道和流
        channel.close();
        channel1.close();

        fileInputStream.close();
        fileOutputStream.close();
    }
}

3、Buffer和Channel的注意事项

五、Selector

六、Selector、Channel和Buffer关系

  • 每个channel都会对应一个Buffer。
  • Selector对应一个线程,一个线程对应多个channel(连接)。
  • 程序切换到那个Channel是由事件决定的,Event就是一个总要的概念。
  • Selector会根据不同的事件,在各个通道上切换。
  • Buffer就是一个内存块,底层是一个数组。
  • 数据的读取写入是通过Buffer。BIO中要么是输入流要么是输出流,不能双向。但是NIO的Buffer是可以读也可以写的,需要调用flip方法切换。
  • channel是双向的,可以返回底层操作系统的情况,比如Linux,底层的操作系统通道就是双向的。
    在这里插入图片描述
package test.nio;

import java.nio.IntBuffer;
public class BasicBuffer {
    public static void main(String[] args) {
        //创建一个buffer, 容量大小为5,即可以存放5个int
        IntBuffer buffer = IntBuffer.allocate(5);

       for (int j = 0; j < 5; j ++) {
           //向buffer中存放数据
           for (int i = 0; i < buffer.capacity(); i++) {
               buffer.put(i * 2);
           }

           //从buffer中读取数据
           buffer.flip();
           while(buffer.hasRemaining()) {
               int i = buffer.get();
               System.out.println(i);
           }
           System.out.println("==============================================================================");
           buffer.flip();
       }
    }
}

输出结果:

0
2
4
6
8
==============================================================================
0
2
4
6
8
==============================================================================
0
2
4
6
8
==============================================================================
0
2
4
6
8
==============================================================================
0
2
4
6
8
==============================================================================

Process finished with exit code 0

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

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

相关文章

吴恩达2022机器学习专项课程(一)8.1 过拟合

目录 什么是过拟合&#xff1f;如何解决过拟合&#xff1f;什么是泛化&#xff1f;它跟过拟合有什么关系&#xff1f;过拟合案例线性回归线性回归的欠拟合线性回归较好的拟合线性回归的过拟合 逻辑回归逻辑回归的欠拟合逻辑回归的较好的拟合逻辑回归的过拟合 总结 什么是过拟合…

Macs Fan Control Pro for mac激活版:macOS 平台的风扇控制软件

Macs Fan Control Pro是一款用于 macOS 平台的风扇控制软件&#xff0c;它允许用户监控和调整 Mac 电脑的风扇转速。以下是该软件的一些特点和功能&#xff1a; Macs Fan Control Pro for mac激活版下载 风扇监控&#xff1a;Macs Fan Control Pro 提供实时的风扇转速监控&…

nn.TransformerEncoderLayer详细解释,使用方法!!

nn.TransformerEncoderLayer nn.TransformerEncoderLayer 是 PyTorch 的 torch.nn 模块中提供的一个类&#xff0c;用于实现 Transformer 编码器的一个单独的层。Transformer 编码器层通常包括一个自注意力机制和一个前馈神经网络&#xff0c;中间可能还包含层归一化&#xff…

SSH和Telnet的区别

SSH&#xff08;Secure Shell&#xff09;和Telnet是两种网络协议&#xff0c;用于远程登录和管理计算机系统。但是它们有以下几个主要的区别&#xff1a; 安全性&#xff1a;SSH是一种加密的协议&#xff0c;可以向服务器传输加密的数据&#xff0c;以防止数据被窃听或篡改。而…

【SSM整合】全注解开发

引入依赖&#xff1a; <dependencies><!--springmvc--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>6.1.4</version></dependency><!--spring jdbc--…

MAC 本地搭建Dify环境

Dify 介绍 Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员&#xff0c;也能参与到 AI 应用的定义和数据运营过…

最小K个数(力扣面试题17.14)

本文采用的是大堆排序求最小的K个值。需要有堆的数据结构基础哦。 代码展示&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ void AdjustDown(int* parr,int n,int root)//向下调整 {int parentroot;int child parent*21;while…

Zynq 7000 系列之启动模式—NAND启动

NAND启动是一种使用NAND闪存进行设备启动的方式。NAND闪存是一种非易失性存储设备&#xff0c;广泛用于嵌入式系统、计算机和其他电子设备中。由于NAND闪存具有高速读写和较高的存储密度等特点&#xff0c;使得NAND启动成为了一种高效且常用的启动方式。 1 特点 NAND启动具有…

短视频交友系统搭建重点,会用到哪些三方服务?

在搭建短视频交友系统时&#xff0c;为了确保系统的稳定性、安全性和用户体验&#xff0c;通常需要用到多种第三方服务。以下是搭建短视频交友系统时可能用到的关键第三方服务&#xff1a; 云服务提供商&#xff1a;如阿里云、腾讯云等&#xff0c;提供稳定、可扩展的服务器资源…

Spring管理第三方依赖

在开发中&#xff0c;我们常需要根据业务需求导入我们需要的第三方依赖包&#xff0c;本文主要以导入druid数据库来连接池为案例讲解有关spring管理第三方依赖 目录 纯注解文件注入 1.在pom.xml中导入依赖 2.在com.lcyy包下建立一个config包用于配置类的实现 3.在config包下…

Python量化炒股的获取数据函数—get_concept()

查询股票所属的概念板块函数get_concept()&#xff0c;利用该函数可以查询一只或多只股票所属的概念板块&#xff0c;其语法格式如下&#xff1a; get_concept(security, dateNone)security&#xff1a;标的代码。类型为字符串&#xff0c;形式如‘000001.XSHE’&#xff0c;或…

ICCV2023人脸识别TransFace论文及代码学习笔记

论文链接&#xff1a;https://arxiv.org/pdf/2308.10133.pdf 代码链接&#xff1a;GitHub - DanJun6737/TransFace: Code of TransFace 背景 尽管ViTs在多种视觉任务中展示了强大的表示能力&#xff0c;但作者发现&#xff0c;当应用于具有极大数据集的人脸识别场景时&#…

Prometheus+Grafana多方位监控

PrometheusGrafana多方位监控 契机 ⚙ 最近发现火山引擎有托管的Prometheus,可是当前是邀测阶段。并且发现火山云的ECS是自带开机自启的exporter的。刚好需要搭建一套服务器监控&#xff0c;所以研究了一套Prometheus监控&#xff0c;包含linux主机监控nginx监控es监控rabbitM…

ITMS-90426: Invalid Swift Support

原文 Please correct the following issues and upload a new binary to App Store Connect. ITMS-90426: Invalid Swift Support - The SwiftSupport folder is missing. Rebuild your app using the current public (GM) version of Xcode and resubmit it. 解决方式 ITMS-…

Keepalived+LVS实现Nginx集群配置

Nginx1和Nginx2组成集群&#xff0c;为了实现负载均衡&#xff0c;在集群的前端配置了LVS服务&#xff0c;但是一台LVS容器产生单点故障&#xff0c;因此需要过Keepalived实现LVS的高可用集群 192.168.136.55node1keepalived192.168.136.56node2keeplived192.168.136.57 node3n…

SoundStream: 下一代的神经网络音频编解码器,实时压缩不牺牲音质

音频编解码技术的目标是&#xff0c;通过减少音频文件的大小来节省存储空间或减轻网络传输的负担。理想的情况下&#xff0c;即使音频被压缩&#xff0c;我们听到的声音与原版也应该没有任何区别。 过去&#xff0c;已经有不少编解码技术被开发出来&#xff0c;满足了这些需求…

Linux基础-socket详解、TCP/UDP

文章目录 一、Socket 介绍二、Socket 通信模型三、Socket 常用函数1 创建套接字2 绑定套接字3、监听连接4、接受连接5、接收和发送数据接收数据发送数据 6、关闭套接字 四、Socket编程试验1、源码server.cclient.c 2、编译&#xff1a;3、执行结果 五、补充TCP和UDP协议的Socke…

无人机+三维建模:倾斜摄影技术详解

无人机倾斜摄影测量技术是一项高新技术&#xff0c;近年来在国际摄影测量领域得到了快速发展。这种技术通过从一个垂直和四个倾斜的五个不同视角同步采集影像&#xff0c;从而获取到丰富的建筑物顶面及侧视的高分辨率纹理。这种技术不仅能够真实地反映地物情况&#xff0c;还能…

3.CQL使用-创建、查询、返回

Neo4j的Cypher语言为处理图形数据而构建的。 CQL代表Cypher查询语言。 https://neo4j.com/docs/cypher-manual/3.5/clauses/load-csv/ CQL语句使用文档地址。 小技巧&#xff1a;在neo4j的CQL语句输入框里&#xff0c;shift enter换行&#xff0c;直接enter键会执行CQL语…

2024智能科学与软件工程国际学术会议(ICISSE 2024)

2024智能科学与软件工程国际学术会议&#xff08;ICISSE 2024) 会议简介 2024智能科学与软件工程国际学术会议&#xff08;ICISSE 2024&#xff09;将在北京隆重举行。本次会议汇集了全球智能科学和软件工程领域的专家学者&#xff0c;共同探讨该领域的最新研究成果和发展趋…