当前位置: 首页 > 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;这让家长们不得不重视孩子的视力健康问题。那么孩子学习使用的台灯应该怎…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...