Java NIO、BIO介绍

article/2025/11/8 7:11:35

Java BIO 基本介绍

  1. I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能。
  2. Java 共支持 3 种网络编程模型 I/O 模式:BIO、NIO、AIO。
  3. Java BIO:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】

Java BIO 模型

在这里插入图片描述
在这里插入图片描述

Java BIO 应用实例

实例说明:

  1. 使用 BIO 模型编写一个服务器端,监听 6666 端口,当有客户端连接时,就启动一个线程与之通讯。
  2. 要求使用线程池机制改善,可以连接多个客户端。
  3. 服务器端可以接收客户端发送的数据(telnet 方式即可)。
  4. 代码演示:
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class BIOServer {public static void main(String[] args) throws Exception {//线程池机制//思路//1. 创建一个线程池//2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();//创建ServerSocketServerSocket serverSocket = new ServerSocket(6666);System.out.println("服务器启动了");while (true) {System.out.println("线程信息id = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());//监听,等待客户端连接System.out.println("等待连接....");//会阻塞在accept()final Socket socket = serverSocket.accept();System.out.println("连接到一个客户端");//就创建一个线程,与之通讯(单独写一个方法)newCachedThreadPool.execute(new Runnable() {public void run() {//我们重写//可以和客户端通讯handler(socket);}});}}//编写一个handler方法,和客户端通讯public static void handler(Socket socket) {try {System.out.println("线程信息id = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());byte[] bytes = new byte[1024];//通过socket获取输入流InputStream inputStream = socket.getInputStream();//循环的读取客户端发送的数据while (true) {System.out.println("线程信息id = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());System.out.println("read....");int read = inputStream.read(bytes);if (read != -1) {System.out.println(new String(bytes, 0, read));//输出客户端发送的数据} else {break;}}} catch (Exception e) {e.printStackTrace();} finally {System.out.println("关闭和client的连接");try {socket.close();} catch (Exception e) {e.printStackTrace();}}}
}

在这里插入图片描述

问题:

  1. 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write。
  2. 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。

Java NIO 基本介绍

  1. Java NIO 全称 Java non-blocking IO,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 NewIO),是同步非阻塞的。

  2. NIO 相关类都被放在 java.nio 包及子包下,并且对原 java.io 包中的很多类进行改写。【基本案例】

  3. NIO 有三大核心部分:Channel(通道)Buffer(缓冲区)Selector(选择器)

  4. NIO面向缓冲区,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络。

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

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

  7. HTTP 2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP 1.1 大了好几个数量级。

Java NIO 模型

在这里插入图片描述

NIO 三大核心原理示意图

在这里插入图片描述

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

NIO网络编程应用实例 - 群聊系统

实例要求:

  1. 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
  2. 实现多人群聊
  3. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  4. 客户端:通过 Channel 可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)
  5. 目的:进一步理解 NIO 非阻塞网络编程机制
  6. 示意图分析和代码
    在这里插入图片描述
//服务端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;public class GroupChatServer {//定义属性private Selector selector;private ServerSocketChannel listenChannel;private static final int PORT = 6667;//构造器//初始化工作public GroupChatServer() {try {//得到选择器selector = Selector.open();//ServerSocketChannellistenChannel = ServerSocketChannel.open();//绑定端口listenChannel.socket().bind(new InetSocketAddress(PORT));//设置非阻塞模式listenChannel.configureBlocking(false);//将该 listenChannel 注册到 selectorlistenChannel.register(selector, SelectionKey.OP_ACCEPT);} catch (IOException e) {e.printStackTrace();}}public void listen() {try {//循环处理while (true) {int count = selector.select();if (count > 0) { //有事件处理// 遍历得到 selectionKey 集合Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {//取出 selectionkeySelectionKey key = iterator.next();//监听到 acceptif (key.isAcceptable()) {SocketChannel sc = listenChannel.accept();sc.configureBlocking(false);//将该 sc 注册到 seletorsc.register(selector, SelectionKey.OP_READ);//提示System.out.println(sc.getRemoteAddress() + " 上线 ");}if (key.isReadable()) {//通道发送read事件,即通道是可读的状态// 处理读(专门写方法..)readData(key);}//当前的 key 删除,防止重复处理iterator.remove();}} else {System.out.println("等待....");}}} catch (Exception e) {e.printStackTrace();} finally {//发生异常处理....}}//读取客户端消息public void readData(SelectionKey key) {SocketChannel channel = null;try {//得到 channelchannel = (SocketChannel) key.channel();//创建 bufferByteBuffer buffer = ByteBuffer.allocate(1024);int count = channel.read(buffer);//根据 count 的值做处理if (count > 0) {//把缓存区的数据转成字符串String msg = new String(buffer.array());//输出该消息System.out.println("form客户端:" + msg);//向其它的客户端转发消息(去掉自己),专门写一个方法来处理sendInfoToOtherClients(msg, channel);}} catch (IOException e) {try {System.out.println(channel.getRemoteAddress() + "离线了..");//取消注册key.cancel();//关闭通道channel.close();} catch (IOException e2) {e2.printStackTrace();}}}//转发消息给其它客户(通道)private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {System.out.println("服务器转发消息中...");//遍历所有注册到 selector 上的 SocketChannel,并排除 selffor (SelectionKey key : selector.keys()) {//通过 key 取出对应的 SocketChannelChannel targetChannel = key.channel();//排除自己if (targetChannel instanceof SocketChannel && targetChannel != self) {//转型SocketChannel dest = (SocketChannel) targetChannel;//将 msg 存储到 bufferByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());//将 buffer 的数据写入通道dest.write(buffer);}}}public static void main(String[] args) {//创建服务器对象GroupChatServer groupChatServer = new GroupChatServer();groupChatServer.listen();}
}// 客户端: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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;public class GroupChatClient {//定义相关的属性private final String HOST = "127.0.0.1";//服务器的ipprivate final int PORT = 6667;//服务器端口private Selector selector;private SocketChannel socketChannel;private String username;//构造器,完成初始化工作public GroupChatClient() throws IOException {selector = Selector.open();//连接服务器socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));//设置非阻塞socketChannel.configureBlocking(false);//将 channel 注册到selectorsocketChannel.register(selector, SelectionKey.OP_READ);//得到 usernameusername = socketChannel.getLocalAddress().toString().substring(1);System.out.println(username + " is ok...");}//向服务器发送消息public void sendInfo(String info) {info = username + " 说:" + info;try {socketChannel.write(ByteBuffer.wrap(info.getBytes()));} catch (IOException e) {e.printStackTrace();}}//读取从服务器端回复的消息public void readInfo() {try {int readChannels = selector.select();if (readChannels > 0) {//有可以用的通道Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();if (key.isReadable()) {//得到相关的通道SocketChannel sc = (SocketChannel) key.channel();//得到一个 BufferByteBuffer buffer = ByteBuffer.allocate(1024);//读取sc.read(buffer);//把读到的缓冲区的数据转成字符串String msg = new String(buffer.array());System.out.println(msg.trim());}}iterator.remove(); //删除当前的 selectionKey,防止重复操作} else {//System.out.println("没有可以用的通道...");}} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) throws Exception {//启动我们客户端GroupChatClient chatClient = new GroupChatClient();//启动一个线程,每个 3 秒,读取从服务器发送数据new Thread() {public void run() {while (true) {chatClient.readInfo();try {Thread.currentThread().sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();//发送数据给服务器端Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String s = scanner.nextLine();chatClient.sendInfo(s);}}
}

NIO 零拷贝案例

案例要求:

  1. 使用传统的 IO 方法传递一个大文件
  2. 使用 NIO 零拷贝方式传递(transferTo)一个大文件
  3. 看看两种传递方式耗时时间分别是多少
NewIOServer.javaimport java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;//服务器
public class NewIOServer {public static void main(String[] args) throws Exception {InetSocketAddress address = new InetSocketAddress(7001);ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();ServerSocket serverSocket = serverSocketChannel.socket();serverSocket.bind(address);//创建bufferByteBuffer byteBuffer = ByteBuffer.allocate(4096);while (true) {SocketChannel socketChannel = serverSocketChannel.accept();int readcount = 0;while (-1 != readcount) {try {readcount = socketChannel.read(byteBuffer);} catch (Exception ex) {// ex.printStackTrace();break;}//byteBuffer.rewind(); //倒带 position = 0 mark 作废}}}
}NewIOClient.javaimport java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;//客户端
public class NewIOClient {public static void main(String[] args) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 7001));String filename = "protoc-3.6.1-win32.zip";//得到一个文件channelFileChannel fileChannel = new FileInputStream(filename).getChannel();//准备发送long startTime = System.currentTimeMillis();//在 linux 下一个 transferTo 方法就可以完成传输//在 windows 下一次调用 transferTo 只能发送 8m, 就需要分段传输文件,而且要主要//传输时的位置=》课后思考...//transferTo 底层使用到零拷贝long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);System.out.println("发送的总的字节数 = " + transferCount + " 耗时: " + (System.currentTimeMillis() - startTime));//关闭fileChannel.close();}
}

BIO、NIO、AIO 对比表

-BIONIOAIO
IO模型同步阻塞同步非阻塞(多路复用)异步非阻塞
编程难度简单复杂复杂
可靠性
吞吐量
举例说明
  1. 同步阻塞:到理发店理发,就一直等理发师,直到轮到自己理发。
  2. 同步非阻塞:到理发店理发,发现前面有其它人理发,给理发师说下,先干其他事情,一会过来看是否轮到自己.
  3. 异步非阻塞:给理发师打电话,让理发师上门服务,自己干其它事情,理发师自己来家给你理发

http://chatgpt.dhexx.cn/article/EOHulHqE.shtml

相关文章

BIO和NIO详解

到底什么是“IO Block” 很多人说BIO不好&#xff0c;会“block”&#xff0c;但到底什么是IO的Block呢&#xff1f;考虑下面两种情况&#xff1a; 用系统调用read从socket里读取一段数据用系统调用read从一个磁盘文件读取一段数据到内存 如果你的直觉告诉你&#xff0c;这两…

Java基础知识——BIO模式

文章目录 一、Java的I/O总述1、I/O模型2、应用 二、BIO模式1、传统的服务器、客户端通信(一对一)&#xff1a;2、服务器和客户端的通信(一对多)3、伪异步IO编程4、BIO模式下的文件上传 一、Java的I/O总述 I/O模型&#xff1a;就是用什么样的通道或者通信模式和架构进行数据的传…

块设备驱动、bio理解

别人写过的内容&#xff0c;我就不写了。贴一下大佬的博客&#xff0c;写的非常好&#xff1a; 块设备驱动实战基础篇一 &#xff08;170行代码构建一个逻辑块设备驱动&#xff09; 块设备驱动实战基础篇二 &#xff08;继续完善170行过滤驱动代码至200行&#xff09; 块设备…

Java Bio编程

IO模型 基本说明 io模型就是数据的发送与接收&#xff0c;这个直接决定了程序之间通信的效率Java的网络编程常见三种Io分别是&#xff1a;bio&#xff0c;nio&#xff0c;aioBio&#xff1a;阻塞并且同步&#xff0c;服务器实现一个连接对应一个线程&#xff0c;如果这个连接…

JAVA BIO 编程

1. JAVA BIO基本介绍 Java BIO 就是传统的 java io 编程&#xff0c;其相关的类和接口在 java.io;BIO(blocking I/O) &#xff1a; 同步阻塞&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器端就需要启动一个线程进行处理&#xff0c;如果…

Java BIO

BIO通信模型 BIO通信服务端&#xff0c;通常有一个独立的Acceptor线程负责监听客户端的连接。接收到客户端连接请求后会为每个客户端创建一个新的线程进行链路处理&#xff0c;处理完成后返回应答给客户端&#xff0c;也就是经典的请求&#xff0d;应答通信模型。但是随着客户端…

BIO和NIO

两种通信模式BIO和NIO ​ io是指计算机的输入输出操作&#xff0c;广义的讲就是数据在的一种传输&#xff0c;可分为磁盘io&#xff08;硬盘的读写&#xff09;和网络io&#xff08;socket的读写&#xff09;&#xff0c;这里的两种模式都是基于网络io的。 io的分类 阻塞I/O…

BIO、NIO、AIO详解

一、Java的I/O演进之路 Java共支持3种网络编程的I/O模型&#xff1a;BIO、NIO、AIO BIO&#xff1a; 同步并阻塞&#xff08;传统阻塞型&#xff09;&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器端就需要启动一个线程进行处理&…

OpenSSL BIO源码简析

文章目录 1. BIO简介BIO chainBIO数据结构BIO_METHOD数据结构 2. Base64示例分析初始化构造BIO链写数据free 1. BIO简介 相关文档 /html/man7/bio.html /html/man3/BIO_*.htmlbio - Basic I/O abstraction&#xff0c;即IO抽象层。 BIO有两种: source/sink BIO&#xff0c;…

二、JAVA BIO

NIO 目录 文章目录 二、JAVA BIO1、 Java BIO基本介绍2、 java BIO工作机制3、传统的BIO编程实例回顾3.1、客户端案例如下3.2、服务端案例如下3.3、输出3.4、小结 4、BIO模式下多发和多收消息4.1、客户端代码如下4.2、服务端代码如下4.3、输出 5、BIO模式下接收多个客户端5.1、…

BIO和NIO的区别

1.BIO基本介绍 BIO是传统的Java IO编程&#xff0c;其基本的类和接口在java.io包中BIO(blocking I/O)&#xff1a;同步阻塞&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器端就需要启动一个线程进行处理&#xff0c;如果这个连接不做任何…

网络编程之中篇——BIO模型详述

1、BIO介绍 1.1、BIO的概念 BIO&#xff08;Blocking IO&#xff09;同步阻塞IO模型&#xff0c;在JDK 1.4之前&#xff0c;建立网络链接采用的只有BIO的模型 需要服务端首先启动建立一个ServerSocket实例&#xff0c;然后客户端启动Socket实例对服务端进行连接通信&#xf…

BIO,NIO,AIO分别是什么?他们有什么区别?

1、BIO 概念&#xff1a; BIO是一种同步阻塞I/O模式&#xff0c;服务实现模式为一个连接对应一个线程&#xff0c;即客户端发送一个连接&#xff0c;服务端要有一个线程来处理。 存在的问题&#xff1a; 一旦有高并发的大量请求,就会有如下问题&#xff1a; 1&#xff09;线程…

JAVA BIO与NIO、AIO的区别(这个容易理解)

IO的方式通常分为几种&#xff0c;同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。 一、BIO 在JDK1.4出来之前&#xff0c;我们建立网络连接的时候采用BIO模式&#xff0c;需要先在服务端启动一个ServerSocket&#xff0c;然后在客户端启动Socket来对服务端进行通信&#…

Quartus II 上手攻略

第一次接触EDA实验&#xff0c;对这方面的相关操作并不熟悉。本篇文章结合上课内容和B站Quartus进行整理&#xff0c;总结一下Quartus 这款软件的基本使用。 参考的B站教学链接&#xff1a;《Quartus II 软件安装与入门教程》 Quartus 软件简介 Quartus II 是Altera公司为其FP…

完全卸载quartus ii 9.0

即将毕业了&#xff0c;把电脑一些不用的软件清清&#xff0c;发现quartus软件贼占空间&#xff0c;删除又貌似找不到卸载的exe&#xff0c;百度了好多都不靠谱 下面介绍一种方法&#xff0c;可以很好的卸载掉quartus&#xff0c;原先我的quartus是安装在D盘下&#xff0c;结果…

Quartus II与Modelsim软件安装教程

Quartus II与Modelsim软件安装教程 一、Quartus II软件安装1、Quartus II安装2、器件安装3、Quartus 破解4、USB Blaster 驱动安装 二、Modelsim软件安装1、modelsim安装2、modelsim注册 三、参考资料 一、Quartus II软件安装 本节主要讲述Quartus II13.1软件的安装使用&#x…

Quartus II13.1安装教程

安装前先关闭杀毒软件和360卫士&#xff0c;注意安装路径不能有中文&#xff0c;安装包路径也不要有中文。 1.鼠标右击【Quartus II 13.1】压缩包选择【解压到Quartus II 13.1】。 2.双击打开解压后的【Quartus II 13.1】文件夹。 3.双击打开【Quartus】文件夹。 4.鼠标右击【Q…

Quartus II下载器件库

Quartus II下载器件库 1、在浏览器中输入网址 https://fpgasoftware.intel.com/18.1/?editionstandard&platformwindows&#xff0c; 或https://fpgasoftware.intel.com/ 进入如下图所示界面。 2、在版本类型和版本中输入Quartus II所对应的版本 3、输入完版本后&#…

Quartus II软件的使用

在这里&#xff0c;我们只是简单的介绍了一下上述的流程图&#xff0c;让大家有个大致的了解&#xff0c;接下来我们就以流水灯实验的工程为例&#xff0c;对每个流程进行详细的操作演示&#xff0c;一步步、手把手带领大家学习使用Quartus II软件。 在创建工程之前&#xff0c…