当前位置: 首页 > news >正文

I/O 模型学习笔记【全面理解BIO/NIO/AIO】

文章目录

  • I/O 模型
    • 什么是 I/O 模型
    • Java支持3种I/O模型
      • BIO(Blocking I/O)
      • NIO(Non-blocking I/O)
      • AIO(Asynchronous I/O)
    • BIO、NIO、AIO适用场景分析
  • java BIO
    • Java BIO 基本介绍
    • Java BIO 编程流程
    • 一个栗子
      • 实现内容:
      • telnet测试
  • java NIO
    • Java NIO 基本介绍
    • 原理图
    • NIO 三大核心-缓冲区(Buffer)
      • 基本说明
      • 缓冲区的类型
      • 缓冲区的创建:
      • 缓冲区的属性:
      • 缓冲区的读写:
      • 缓冲区的模式:
      • 缓冲区的使用:
    • NIO 三大核心-通道(Channel)
      • 基本说明
      • 通道的类型
      • 通道的创建:
      • 读取和写入数据
      • 通道的关闭
      • 定位和截断
      • 传输数据
      • 应用实例
    • NIO 三大核心-选择器(Selector)
      • 基本说明
      • 基本原理
      • 选择器的创建:
      • 通道的注册:
      • 选择就绪的通道:
      • 处理就绪的通道:
      • 取消注册:
    • NIO 网络编程示例
      • 一个简单的聊天室服务器和客户端
      • 测试
    • NIO与零拷贝
      • 零拷贝的作用
      • 零拷贝关键概念和常见的实现方式

I/O 模型

什么是 I/O 模型

I/O 模型可以简单理解为决定了数据如何在程序和外部环境(例如磁盘、网络等)之间进行发送和接收的方式,从而直接影响了程序通信的性能。不同的 I/O 模型可以以不同的方式管理数据的传输和处理,这些方式可以在性能、并发性和可维护性等方面产生重要影响。

Java支持3种I/O模型

BIO(Blocking I/O)

BIO 是传统的阻塞式 I/O 模型,它的特点是当一个线程执行一个 I/O 操作时,它会被阻塞,直到操作完成。这种模型在编程上比较简单,但在高并发环境下性能较差,因为每个连接都需要一个独立的线程,会导致资源消耗过大。
在这里插入图片描述

NIO(Non-blocking I/O)

NIO 是新的非阻塞 I/O 模型,它使用了 Java NIO 包中的通道(Channel)和缓冲区(Buffer)来实现。NIO 支持多路复用技术,允许一个线程监视多个通道的状态,从而更高效地处理多个连接。NIO 在高并发环境中表现更出色,但编程复杂度相对较高。
在这里插入图片描述

AIO(Asynchronous I/O)

AIO 是异步 I/O 模型,它通过 Java NIO 2 中的异步通道(AsynchronousChannel)来实现。AIO 允许程序发起 I/O 操作后继续执行其他任务,而不需要轮询操作状态,当操作完成时会通知程序。这种模型适用于需要高度并发和异步操作的场景,但编程复杂性也相对较高。

BIO、NIO、AIO适用场景分析

1, BIO(Blocking I/O):

  • 适用场景:BIO 适合于简单的、并发要求不高的应用程序,特别是在单线程或低并发环境中。它的编程模型较为简单,易于理解和实现。常见的应用包括文件 I/O 操作、简单的网络通信以及需要处理少量并发连接的服务器。
  • 优点:简单易用,适合初学者;适用于并发要求不高的场景。
  • 缺点:在高并发环境中性能不佳,因为每个连接都需要一个独立的线程,导致资源消耗较高。

2, NIO(Non-blocking I/O):

  • 适用场景:NIO 适合需要处理大量并发连接的服务器应用,例如聊天服务器、Web服务器、代理服务器等。它也适用于需要高性能和响应时间的应用。NIO 的非阻塞特性可以更高效地管理多个连接。
  • 优点:适用于高并发场景;性能相对较高;支持多路复用。
  • 缺点:编程复杂,对开发者要求较高;需要精细的事件处理。

3, AIO(Asynchronous I/O):

  • 适用场景:AIO 适合需要进行异步操作的应用,尤其是需要高度并发、可扩展性和高性能的应用。这包括高性能的网络服务器、数据库访问、大规模文件处理等。
  • 优点:异步操作,可处理大量并发连接;适用于高性能和高并发需求;避免了轮询。
  • 缺点:编程复杂度较高,需要熟练掌握异步编程概念;在某些情况下,可能会增加代码的复杂性。

java BIO

Java BIO 基本介绍

Java BIO(Blocking I/O,阻塞式I/O)是Java传统的I/O模型,它的工作方式基于阻塞操作。在Java BIO中,I/O操作会导致程序被阻塞,直到操作完成。以下是关于Java BIO的基本介绍:

  • 阻塞模型:在Java BIO中,当程序执行I/O操作时,它会被阻塞,直到操作完成。这意味着程序无法执行其他任务,直到I/O操作完成或发生超时。

  • 同步操作:BIO的I/O操作是同步的,这意味着程序在发出I/O请求后需要等待直到数据准备就绪或写入完成。这通常涉及到使用Java的输入流和输出流来进行读取和写入操作。

  • 适用性:BIO适用于相对简单的I/O操作,特别是在单线程或低并发环境中。例如,它可以用于文件I/O操作,网络通信,或者处理少量并发连接的服务器。

  • 资源消耗:BIO的一个主要问题是资源消耗。每个连接都需要一个独立的线程,这在高并发情况下会导致线程资源的浪费和性能下降。大量线程的创建和管理也可能导致系统开销增加。

工作原理图
在这里插入图片描述

Java BIO 编程流程

  1. 创建ServerSocket:在服务器端,首先需要创建一个 ServerSocket 对象,它用于监听客户端的连接请求。通常,你需要指定服务器监听的端口号。
ServerSocket serverSocket = new ServerSocket(port);
  1. 等待客户端连接:使用 ServerSocket 的 accept() 方法,服务器会一直阻塞等待客户端连接请求。当有客户端请求连接时,accept() 方法返回一个 Socket 对象,代表与客户端的通信通道。
Socket clientSocket = serverSocket.accept();
  1. 创建输入输出流:一旦客户端连接被接受,你可以通过 Socket 对象获取输入流和输出流,以进行数据的读取和写入。通常,使用 InputStream 和 OutputStream。
InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
  1. 数据读取和写入:使用输入流和输出流,你可以从客户端读取数据和向客户端写入数据。数据的读写操作是阻塞的,这意味着它们会一直等待数据准备就绪或数据写入完成。
byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer);
outputStream.write(buffer, 0, bytesRead);
  1. 关闭连接:当通信完成时,你应该关闭连接,释放资源。首先关闭输入流、输出流、然后关闭Socket,最后关闭 ServerSocket。
inputStream.close();
outputStream.close();
clientSocket.close();
serverSocket.close();

这是一个简单的Java BIO编程流程的示例,它展示了如何创建服务器、等待客户端连接、进行数据传输,最后关闭连接。需要注意的是,该示例是单线程的,对于多个客户端连接,你需要创建多个线程来处理每个连接,这也是BIO模型在高并发环境下性能较差的原因之一。

一个栗子

实现内容:

创建了一个服务器,监听6666端口,并使用线程池处理多个客户端连接。每当有客户端连接时,会创建一个新的ClientHandler线程来处理连接。这种方式可以同时处理多个客户端连接,提高了服务器的并发性能。

public class BioTest {public static void main(String[] args) {final int port = 6666;final int poolSize = 10; // 线程池大小// 创建线程池ExecutorService executorService = Executors.newFixedThreadPool(poolSize);try {// 创建ServerSocket并绑定到指定端口ServerSocket serverSocket = new ServerSocket(port);System.out.println("Server listening on port " + port);while (true) {// 等待客户端连接请求,当有连接请求时,accept方法返回一个Socket对象Socket clientSocket = serverSocket.accept();System.out.println("Accepted connection from " + clientSocket.getRemoteSocketAddress());// 使用线程池处理客户端连接executorService.execute(new ClientHandler(clientSocket));}} catch (IOException e) {e.printStackTrace();}}// 客户端处理线程static class ClientHandler implements Runnable {private Socket clientSocket;public ClientHandler(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {// 获取输入流和输出流InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream();// 读取客户端发送的数据byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {// 将数据原样写回客户端outputStream.write(buffer, 0, bytesRead);System.out.println("get message:"+new String(buffer, 0, bytesRead));}// 关闭连接clientSocket.close();System.out.println("Connection closed");} catch (IOException e) {e.printStackTrace();}}}}

telnet测试

  • 打开本地cmd输入
telnet 127.0.0.1 6666 

在这里插入图片描述

  • 输入ctrl+]调出Telnet命令行
ctrl+]
  • send 命令发送测试
send hello

在这里插入图片描述

java NIO

Java NIO 基本介绍

Java NIO(New I/O,新I/O)是Java编程语言中用于非阻塞I/O操作的一组API。它提供了一种更灵活、高效的I/O处理方式,适用于高并发和高性能的网络通信和文件操作。以下是Java NIO的基本介绍:

  • 非阻塞模型:Java NIO采用了非阻塞I/O模型,与传统的阻塞I/O(BIO)不同,它不会导致线程在I/O操作中阻塞,而是可以继续执行其他任务,从而提高并发性能。

  • 通道和缓冲区:NIO引入了通道(Channel)和缓冲区(Buffer)的概念。通道是数据的源或目标,缓冲区用于在通道和应用程序之间传输数据。通道可以是文件、套接字、管道等。

  • 选择器(Selector):Java NIO还引入了选择器,用于多路复用I/O操作。选择器可以同时管理多个通道,监测它们的状态,一旦有通道准备好执行读或写操作,选择器就会通知应用程序进行处理,而不需要轮询。

  • 多线程:Java NIO适用于多线程环境。多个线程可以同时监听和处理多个通道,从而实现高并发的I/O操作。

  • 适用性:Java NIO适用于需要高并发、高性能的网络通信应用,例如Web服务器、代理服务器、聊天服务器等。它还适用于需要高效文件操作的应用。

  • 高性能:由于非阻塞模型和多路复用技术,Java NIO通常具有较高的性能,可以处理大量并发连接。

  • 编程复杂性:相对于传统的BIO,Java NIO的编程复杂性较高,需要更多的编码工作。但它提供了更灵活的控制和更高的性能。

原理图

在这里插入图片描述关系图的说明:
1,每个channel 都会对应一个Buffer
2,Selector 对应一个线程, 一个线程对应多个channel(连接)
3,该图反应了有三个channel 注册到 该selector //程序
4,程序切换到哪个channel 是有事件决定的, Event 就是一个重要的概念
5,Selector 会根据不同的事件,在各个通道上切换
6,Buffer 就是一个内存块 , 底层是有一个数组
7,数据的读取写入是通过Buffer, 这个和BIO , BIO 中要么是输入流,或者是输出流, 不能双向,但是NIO的Buffer 是可以读也可以写, 需要 flip 方法切换
8,channel 是双向的, 可以返回底层操作系统的情况, 比如Linux , 底层的操作系统通道就是双向的.

NIO 三大核心-缓冲区(Buffer)

基本说明

缓冲区(Buffer)是Java NIO 中的核心组件之一,用于在通道(Channel)和应用程序之间传输数据。缓冲区是一个固定大小的内存区域,它可以保存各种类型的数据,如字节、字符、整数等。缓冲区具有一些重要的属性和方法,用于管理数据的读取和写入。

缓冲区的类型

Java NIO 提供了不同类型的缓冲区,每种类型适用于不同数据类型的读写。常见的缓冲区类型包括:

  • ByteBuffer:用于字节数据。
  • CharBuffer:用于字符数据。
  • ShortBuffer、IntBuffer、LongBuffer:用于整数数据。
  • FloatBuffer、DoubleBuffer:用于浮点数数据。

缓冲区的创建:

  • 缓冲区可以使用静态工厂方法创建,通常通过allocate()方法分配指定大小的缓冲区。
ByteBuffer buffer = ByteBuffer.allocate(1024); // 创建一个1KB的字节缓冲区

缓冲区的属性:

缓冲区有一些重要属性,包括容量(capacity)、位置(position)、限制(limit)和标记(mark)。

  • 容量:缓冲区的总容量,即它可以保存的数据大小。
  • 位置:当前读或写的位置,通常从0开始。
  • 限制:限制了从缓冲区读取或写入的位置,通常等于容量,但可以通过设置进行更精确的控制。
  • 标记:一个可选的标记位置,通常用于记录某个位置,以后可以通过reset()方法返回到这个位置。

缓冲区的读写:

缓冲区提供了一系列读取和写入数据的方法,通常包括put()用于写入数据,get()用于读取数据,flip()用于切换读写模式,rewind()用于重新读取,以及clear()和compact()等用于清空缓冲区或重新组织数据的方法。

缓冲区的模式:

缓冲区有两种主要模式:读模式和写模式。在读模式下,你可以从缓冲区读取数据;在写模式下,你可以向缓冲区写入数据。使用flip()方法可以在读写模式之间切换。

缓冲区的使用:

  • 通常,编写NIO程序的过程包括创建缓冲区、写入数据到缓冲区、从缓冲区读取数据,以及确保在读写模式之间正确切换。
  • 缓冲区是与通道一起使用的,通过通道进行数据的读写。
ByteBuffer buffer = ByteBuffer.allocate(1024);// 写入数据到缓冲区
buffer.put("Hello, World!".getBytes());// 切换到读模式,准备从缓冲区读取数据
buffer.flip();// 从缓冲区读取数据
while (buffer.hasRemaining()) {System.out.print((char) buffer.get());
}// 清空缓冲区,为下一次写入数据做准备
buffer.clear();

NIO 三大核心-通道(Channel)

基本说明

通道(Channel)是Java NIO(New I/O)中的核心概念之一,它用于在数据源和数据目标之间进行双向数据传输。通道通常与缓冲区(Buffer)结合使用,以实现高效的数据读取和写入。

通道的类型

  • Java NIO提供了不同类型的通道,用于不同的数据源和数据目标,包括文件、网络套接字、管道等。
  • 常见的通道类型包括FileChannel、SocketChannel、ServerSocketChannel和DatagramChannel。

通道的创建:

  • 通道通常通过工厂方法来创建,具体取决于通道类型。
  • 以下是创建FileChannel和SocketChannel的示例:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;// 创建FileChannel
FileChannel fileChannel = new FileInputStream("source.txt").getChannel();// 创建SocketChannel
SocketChannel socketChannel = SocketChannel.open();

读取和写入数据

  • 通道通常与缓冲区一起使用,用于数据的读取和写入。数据可以从通道读入缓冲区,或从缓冲区写入通道。
  • 以下是使用FileChannel进行数据读写的示例:
ByteBuffer buffer = ByteBuffer.allocate(1024);// 从FileChannel读取数据到缓冲区
int bytesRead = fileChannel.read(buffer);// 将数据从缓冲区写入FileChannel
buffer.flip(); // 切换到读模式
while (buffer.hasRemaining()) {fileChannel.write(buffer);
}

通道的关闭

  • 通道使用后应该显式关闭以释放资源。通常使用close()方法来关闭通道。
  • 通道的关闭是非常重要的,以避免资源泄漏。
fileChannel.close();

定位和截断

  • 通道具有当前位置,通过position()和position(long pos)方法来查询和设置。
  • FileChannel还支持文件指针的设置,可以使用position(long pos)方法。
long currentPosition = fileChannel.position(); // 查询当前位置
fileChannel.position(1024); // 设置新位置

传输数据

通道支持直接数据传输,通过transferTo()和transferFrom()方法在通道之间传输数据。

FileChannel sourceChannel = new FileInputStream("source.txt").getChannel();
FileChannel destChannel = new FileOutputStream("destination.txt").getChannel();
sourceChannel.transferTo(0, sourceChannel.size(), destChannel);

应用实例

实例要求:
使用 FileChannel(通道) 和 方法 transferFrom ,完成文件的拷贝

拷贝一张图片
代码演示:

public class NIOFileChannel04 {public static void main(String[] args)  throws Exception {//创建相关流FileInputStream fileInputStream = new FileInputStream("d:\\a.png");FileOutputStream fileOutputStream = new FileOutputStream("d:\\a2.png");//获取各个流对应的filechannelFileChannel sourceCh = fileInputStream.getChannel();FileChannel destCh = fileOutputStream.getChannel();//使用transferForm完成拷贝destCh.transferFrom(sourceCh,0,sourceCh.size());//关闭相关通道和流sourceCh.close();destCh.close();fileInputStream.close();fileOutputStream.close();}
}

NIO 三大核心-选择器(Selector)

基本说明

选择器(Selector)是Java NIO中的一个关键组件,它允许单个线程来监视多个通道的I/O事件,从而实现高效的非阻塞I/O操作。选择器的主要作用是管理多个通道的事件,如读就绪、写就绪、连接就绪等,并且能够提供一种有效的方式,以便在这些通道之间切换。
在这里插入图片描述

基本原理

选择器(Selector)的主要原理是使用单个线程来管理多个客户端连接的I/O事件。这种机制称为多路复用(Multiplexing)。以下是多路复用选择器的基本原理:

  • 单线程管理多个通道:选择器创建一个线程,该线程负责管理多个通道,监视它们的I/O事件。这些通道可以是SocketChannel、ServerSocketChannel、DatagramChannel等。线程使用选择器来注册并监视这些通道。

  • 事件通知:当一个或多个通道上的I/O事件发生时(如有数据可读、连接已建立、可写等),选择器会通知线程,而不需要线程轮询检查每个通道。

  • 事件处理:线程接收到事件通知后,可以使用选择器提供的方法获取就绪的通道和事件类型(如可读、可写),然后执行相应的处理逻辑。这允许一个线程有效地处理多个通道的事件。

  • 非阻塞操作:选择器的通道通常被设置为非阻塞模式,因此即使没有数据可读或可写,线程也不会阻塞在通道上。这使得线程可以同时处理多个通道,而不必等待每个通道的I/O操作完成。

  • 多路复用:选择器通过在一个线程中管理多个通道,实现了多路复用,使得一个线程可以高效地处理多个客户端连接,而不需要为每个连接都创建一个新线程。

选择器的创建:

选择器通常使用Selector.open()来创建。一个选择器可以同时管理多个通道。

import java.nio.channels.Selector;Selector selector = Selector.open();

通道的注册:

  • 通道必须先注册到选择器,以便选择器能够监听其事件。
  • 通道可以注册为读、写、连接、接受等不同类型的事件。
import java.nio.channels.SelectionKey;SelectableChannel channel = ...; // 获取要注册的通道
channel.configureBlocking(false); // 设置通道为非阻塞模式
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

选择就绪的通道:

选择器可以通过select()方法来检查就绪的通道,这些通道有特定类型的事件等待处理。

int readyChannels = selector.select();

处理就绪的通道:

通过遍历选择键(SelectionKey)来处理就绪的通道。选择键中包含了通道和其对应的就绪事件。

Set<SelectionKey> selectedKeys = selector.selectedKeys();
for (SelectionKey key : selectedKeys) {if (key.isReadable()) {// 处理可读事件}if (key.isWritable()) {// 处理可写事件}if (key.isConnectable()) {// 处理连接就绪事件}if (key.isAcceptable()) {// 处理接受连接事件}
}
selectedKeys.clear(); // 清空已处理的选择键

取消注册:

当通道不再需要被选择器管理时,可以取消注册,释放资源。

key.cancel();

以下是一个完整的示例,演示了如何使用选择器和通道进行非阻塞I/O操作:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;/*** @Author: srf* @Date: 2023/10/23 17:33* @description:*/
public class NioTest {public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {int readyChannels = selector.select();if (readyChannels == 0) {continue;}for (SelectionKey key : selector.selectedKeys()) {if (key.isAcceptable()) {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);}if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {clientChannel.close();} else if (bytesRead > 0) {buffer.flip();// 处理从通道读取的数据while (buffer.hasRemaining()) {System.out.print((char) buffer.get());}System.out.println();}}selector.selectedKeys().clear();}}}}

NIO 网络编程示例

一个简单的聊天室服务器和客户端

NIO非阻塞聊天服务器示例

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class ChatServer {public static void main(String[] args) throws IOException {Selector selector = Selector.open();ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(8080));serverSocket.configureBlocking(false);serverSocket.register(selector, SelectionKey.OP_ACCEPT);ByteBuffer buffer = ByteBuffer.allocate(1024);System.out.println("Chat server is running...");while (true) {selector.select();Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);System.out.println("Client connected: " + client.getRemoteAddress());} else if (key.isReadable()) {SocketChannel client = (SocketChannel) key.channel();int bytesRead = client.read(buffer);if (bytesRead == -1) {client.close();} else if (bytesRead > 0) {buffer.flip();String message = new String(buffer.array(), 0, bytesRead);System.out.println("Received: " + message);buffer.clear();// Broadcast the message to all clientsfor (SelectionKey broadcastKey : selector.keys()) {if (broadcastKey.isValid() && broadcastKey.channel() instanceof SocketChannel) {SocketChannel channel = (SocketChannel) broadcastKey.channel();if (channel != client) {buffer.put(message.getBytes());buffer.flip();channel.write(buffer);buffer.clear();}}}}}keyIterator.remove();}}}
}

服务器端接受多个客户端连接,并允许客户端之间进行聊天。服务器接收客户端消息并将其广播给所有连接的客户端。客户端连接到服务器,并可以发送和接收消息。

NIO非阻塞聊天客户端示例

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;public class ChatClient {public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8080));socketChannel.configureBlocking(false);ByteBuffer buffer = ByteBuffer.allocate(1024);Thread receiveThread = new Thread(() -> {try {while (true) {buffer.clear();int bytesRead = socketChannel.read(buffer);if (bytesRead == -1) {socketChannel.close();break;} else if (bytesRead > 0) {buffer.flip();String receivedMessage = new String(buffer.array(), 0, buffer.limit());System.out.println("Received: " + receivedMessage);}}} catch (IOException e) {e.printStackTrace();}});receiveThread.start();Scanner scanner = new Scanner(System.in);while (true) {String message = scanner.nextLine();if ("exit".equalsIgnoreCase(message)) {socketChannel.close();receiveThread.interrupt();break;}buffer.clear();buffer.put(message.getBytes());buffer.flip();socketChannel.write(buffer);}}
}

客户端示例中,我们使用了两个线程:一个用于接收消息,另一个用于发送消息。用户可以输入消息,并在输入"exit"时退出客户端。

测试

  • 先启动服务端,再启动两个客户端
    日志显示建立通信连接
    在这里插入图片描述
  • 客户端1 发送消息,客户端2接受
    在这里插入图片描述
    在这里插入图片描述

NIO与零拷贝

NIO(New I/O)与零拷贝是相关但不同的概念,它们都与高性能I/O操作有关,但在不同的层面和场景下发挥作用。

  • 虽然NIO可以减少数据在Java应用程序中的内存复制,但它并不等同于零拷贝。零拷贝是更低层次的操作,通常需要操作系统或硬件支持,而NIO主要是一种I/O编程模型,侧重于提供更高级别的I/O操作接口,以实现非阻塞、高并发的I/O操作。

  • 在实际应用中,可以将NIO与零拷贝技术结合使用,以最大程度地提高I/O性能。例如,可以使用NIO来管理非阻塞I/O通道,并结合零拷贝技术来执行实际的数据传输,从而在性能和效率上获得双重优势。但零拷贝技术通常需要更深入的系统编程和对底层硬件的了解,因此通常由高级编程语言的底层库或操作系统来实现。

零拷贝的作用

“零拷贝” 这个术语源于它的工作原理:它旨在消除或最小化数据传输中的额外数据拷贝操作,从而减少了对内存和CPU的开销。零拷贝的主要优势在于它避免了在数据传输过程中的以下拷贝操作:

  • 用户空间到内核空间的拷贝: 在传统I/O操作中,数据通常首先从应用程序的用户空间复制到内核空间的缓冲区,然后再从内核空间传输到目标位置(如网络卡、磁盘等)。零拷贝技术避免了这个用户空间到内核空间的复制操作,将数据直接从应用程序的用户空间传输到目标位置。

  • 内核空间到用户空间的拷贝: 在某些情况下,内核可能需要将数据从内核空间的缓冲区复制到用户空间,以便应用程序可以访问它。零拷贝也避免了这种额外的内核到用户空间的复制操作。

  • 中间数据缓冲的拷贝: 零拷贝技术通常避免了将数据从一个缓冲区复制到另一个缓冲区的操作,因为数据可以直接在内存和设备之间传输。

零拷贝关键概念和常见的实现方式

  • 直接内存映射(Direct Memory Mapping)
    • 直接内存映射是一种零拷贝技术,它允许文件内容直接映射到内存,而无需通过中间缓冲区。
    • 操作系统和硬件允许应用程序将文件映射到内存中,使文件数据可以直接在内存和磁盘之间传输,避免了额外的数据复制。
    • Java中,可以使用java.nio.MappedByteBuffer来实现直接内存映射。
  • 文件描述符传输(File Descriptor Transfer):
    • 在某些操作系统中,可以将文件描述符(File Descriptor)从一个进程传递给另一个进程,而不需要将文件数据复制到内存中。
    • 这允许两个进程共享相同的文件,从而避免了数据复制的开销。
    • 在Unix/Linux系统中,可以使用sendfile系统调用来实现文件描述符传输。
  • DMA(Direct Memory Access):
    • DMA是一种硬件支持的技术,它允许外部设备(如网络适配器、磁盘控制器)直接访问内存,而无需CPU的干预。
    • 数据可以直接从外部设备传输到内存,或从内存传输到外部设备,而不需要额外的内存拷贝。
    • DMA通常由操作系统和硬件协同工作来实现。
  • Socket缓冲区传输:
    • 部分操作系统允许将Socket缓冲区中的数据直接传输到另一个Socket缓冲区,而无需将数据从内存复制到Socket缓冲区。
    • 这种技术可用于高性能网络数据传输。

相关文章:

I/O 模型学习笔记【全面理解BIO/NIO/AIO】

文章目录 I/O 模型什么是 I/O 模型Java支持3种I/O模型BIO&#xff08;Blocking I/O&#xff09;NIO&#xff08;Non-blocking I/O&#xff09;AIO&#xff08;Asynchronous I/O&#xff09; BIO、NIO、AIO适用场景分析 java BIOJava BIO 基本介绍Java BIO 编程流程一个栗子实现…...

【Python学习笔记】字符编码

1. 字符串编码 Python3语言里面的字符串对象是unicode字符串&#xff0c;在内存中实际存储时&#xff0c;使用的是 UTF16 编码。但通常不会将UTF16编码的内容写到磁盘或者在网络进行传输&#xff0c; 因为utf16编码比较浪费空间。特别是如果文字信息基本都是英文符号的情况下&…...

华为昇腾NPU卡 大模型LLM ChatGLM2模型推理使用

参考&#xff1a;https://gitee.com/mindspore/mindformers/blob/dev/docs/model_cards/glm2.md#chatglm2-6b 1、安装环境&#xff1a; 昇腾NPU卡对应英伟达GPU卡&#xff0c;CANN对应CUDA底层&#xff1b; mindspore对应pytorch&#xff1b;mindformers对应transformers 本…...

Git 拉取远程更新报错

报错内容如下&#xff1a; cannot lock ref refs/remotes/origin/bugfix/bug: refs/remotes/origin/bugfix 已存在&#xff0c;无法创建 refs/remotes/origin/bugfix/bug 来自 gitlab.zhangyue-inc.com:dejian_ios/iReaderDejian! [新分支] bugfix/bug -> ori…...

腾讯云国际站服务器端口开放失败怎么办?

腾讯云服务器是腾讯公司推出的一种云服务&#xff0c;用户能够经过这种方式在互联网上进行数据存储和计算。然而&#xff0c;用户在运用腾讯云服务器时或许会遇到各种问题&#xff0c;其间端口敞开失利是一个常见问题。本文将具体介绍如何解决腾讯云服务器端口敞开失利的问题。…...

一句话解释什么是出口IP

出口 IP 是指从本地网络连接到公共互联网时所使用的 IP 地址。这个 IP 地址是由 Internet 服务提供商(ISP)分配给你的,它可以用来标识你的网络流量的来源。如果你使用的是 NAT(网络地址转换)技术,则在 NAT 设备内部会进行地址转换,使得多个设备可以共享同一个公共 IP 地…...

深入理解强化学习——强化学习的历史:试错学习

分类目录&#xff1a;《深入理解强化学习》总目录 让我们现在回到另一条通向现代强化学习领域的主线上&#xff0c;它的核心则是试错学习思想。我们在这里只对要点做概述&#xff0c;《深入理解强化学习》系列后面的文章会更详细地讨论这个主题。根据美国心理学家R.S.woodworth…...

分享一个用HTML、CSS和jQuery构建的漂亮的登录注册界面

作为一个前端开发人员&#xff0c;我们经常需要构建用户的登录和注册界面。一个漂亮、用户友好的登录注册界面对于提升用户体验和网站形象至关重要。以下我们使用HTML、CSS和jQuery来做一个漂亮的登录注册界面。 首先&#xff0c;我们需要创建一个html文档&#xff0c;定义登录…...

Java学习 习题 1.

一、 1.2. 3. 4. 5. 二、 1. 2. 3. 4. 5. 6. 7. 8....

第六节——Vue中的事件

一、定义事件 Vue 元素的事件处理和 DOM 元素的很相似&#xff0c;但是有一点语法上的不同 使用修饰符&#xff08;v-on:的缩写&#xff09;事件名的方式 给dom添加事件后面跟方法名&#xff0c;方法名可以直接加括号如click"add()"里面进行传参。对应的事件处理函…...

设置GridView单选

/// <summary> /// 设置GridView单选 /// </summary> /// <param name"view"></param> /// <param name"selectCaption"></param> public static void SetGridViewSingleSel…...

[Python从零到壹] 七十二.图像识别及经典案例篇之OpenGL入门及绘制基本图形和3D图

十月太忙,还是写一篇吧!祝大家1024节日快乐O(∩_∩)O 欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希…...

论文-分布式-并发控制-Lamport逻辑时钟

目录 前言 逻辑时钟讲解 算法类比为面包店内取号 Lamport算法的时间戳原理 Lamport算法的5个原则 举例说明 算法实现 参考文献 前言 在并发系统中&#xff0c;同步与互斥是实现资源共享的关键Lamport面包店算法作为一种经典的解决并发问题的算法&#xff0c;它的实现原…...

长三角实现区块链电子医疗票据互联互通,蚂蚁链提供技术支持

10月25日&#xff0c;记者从浙江省财政厅发布的消息获悉&#xff0c;上海、浙江、江苏和安徽三省一市基于蚂蚁链实现区块链电子医疗票据互联互通&#xff0c;商业保险理赔作为首个规模化应用场景正式落地&#xff0c;蚂蚁保“安心赔”理赔服务率先接入。 今后&#xff0c;老百…...

Redis快速上手篇(三)(事务+Idea的连接和使用)

Redis事务 可以一次执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的 所有命令都会序列化&#xff0c;按顺序地串行化执行而不会被其它命令插入&#xff0c;不许加塞。 单独的隔离的操作 官网说明 https://redis.io/docs/interact/transactions/ MULTI、EXEC、…...

Spring三级缓存解决循环依赖问题

文章目录 1. 三级缓存解决的问题场景2. 三级缓存的差异性3. 循环依赖时的处理流程4. 源码验证 1. 三级缓存解决的问题场景 循环依赖指的是在对象之间存在相互依赖关系&#xff0c;形成一个闭环&#xff0c;导致无法准确地完成对象的创建和初始化&#xff1b;当两个或多个对象彼…...

Unity 中使用波浪动画创建 UI 图像

如何使用 只需将此组件添加到画布中的空对象即可。强烈建议您将此对象放入其自己的画布/嵌套画布中&#xff0c;因为它会弄脏每一帧的画布并导致重新生成整个网格。 注意&#xff1a;不支持切片图像。 using System.Collections.Generic; using UnityEngine; using UnityEng…...

支付功能测试用例测试点?

支付功能测试用例测试点是指在测试支付功能时&#xff0c;需要关注和验证的各个方面。根据不同的支付场景和需求&#xff0c;支付功能测试用例测试点可能有所不同&#xff0c;但一般可以分为以下几类&#xff1a; 功能测试&#xff1a;主要检查支付功能是否符合设计和业务需求…...

HFS 快速搭建 http 服务器

HFS 是一个轻量级的HTTP 服务工具&#xff0c;3.0版本前进提供Windows平台安装包&#xff0c;3.0版本开提供Linux和macOS平台的安装包。 HFS更适合在局域网环境中搭建文件共享服务或者安装配置源服务器。 甲 非守护进程的方式运行 HFS &#xff08;Ubuntu 22.04&#xff09; 一…...

学生专用台灯怎么选?双十一专业学生护眼台灯推荐

台灯应该是很多家庭都会备上一盏的家用灯具&#xff0c;很多大人平时间看书、用电脑都会用上它&#xff0c;不过更多的可能还是给家中的小孩学习、阅读使用的。而且现在的孩子近视率如此之高&#xff0c;这让家长们不得不重视孩子的视力健康问题。那么孩子学习使用的台灯应该怎…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...