Java BIO详解

一、简介

1.1 BIO概述

BIO(Blocking I/O),即同步阻塞IO(传统IO)。

 BIO 全称是 Blocking IO,同步阻塞式IO,是JDK1.4之前的传统IO模型,就是传统的 java.io 包下面的代码实现。

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如下图所示:

在 BIO 模型下,应用程序会在进行 I/O 操作时阻塞当前线程,直到 I/O 操作完成。例如,执行一个读取操作时,线程会等待,直到数据从磁盘或网络中完全读取完成。在这个过程中,线程不能做其他任务,必须等待 I/O 操作的结果。

BIO 模型的特点

  • 同步阻塞:
    • 当线程进行 I/O 操作时,它会被阻塞,直到操作完成。
    • 阻塞操作通常会导致 CPU 的浪费,因为线程在等待 I/O 时并没有进行其他有用的工作。
  • 一个连接一个线程:
    • 每个客户端请求都会创建一个新的线程,每个线程对应一个 I/O 操作。
    • 当并发连接数很多时,系统可能会因为线程数过多而导致性能瓶颈。
  • 适用于连接数较少的场景:BIO 更适用于连接数较少、请求不频繁的应用场景,如一些小型应用或传统的阻塞式通信。
  • 容易实现:相对于其他 I/O 模型(如 NIO 和 AIO),BIO 的实现比较简单,应用开发人员只需要关心 I/O 操作,不需要处理复杂的事件驱动机制。

BIO 的缺点:

  • 线程资源浪费:每个连接都会对应一个独立的线程,当有大量并发连接时,会导致系统开销巨大,因为操作系统会为每个线程分配资源(如内存、栈空间等)。如果并发请求量很大,线程上下文切换开销也会非常高。
  • 不适合高并发:BIO 模型非常依赖操作系统线程,线程数过多时容易造成系统性能下降。特别是在高并发的情况下,线程的创建和销毁频繁,容易耗尽系统资源。
  • 效率低:在 I/O 操作过程中,线程被阻塞,无法处理其他请求,导致 CPU 的浪费。即使没有数据可读或可写,线程依然会等待,直到 I/O 完成。

1.2 IO流概述

IO流是基于流的概念,它将数据的输入和输出看作是一个连续的流。数据从一个地方流向另一个地方,流的方向可以是输入(读取数据)或输出(写入数据)。

IO流的原理是通过流的管道将数据从源头传输到目标地。源头可以是文件、网络连接、内存等,而目标地可以是文件、数据库、网络等。IO流提供了一组丰富的类和方法来实现不同类型的输入和输出操作。

IO流主要用于处理输入和输出操作,适用于以下场景:

  • 文件读写:通过IO流可以读取和写入文件中的数据,如读取配置文件、写入日志等。
  • 网络通信:通过IO流可以进行网络数据的传输和接收,如Socket通信、HTTP请求等。
  • 数据库操作:通过IO流可以将数据读取到内存中,或将内存中的数据写入到数据库中。
  • 文本处理:通过IO流可以读取和写入文本文件,进行文本处理和操作。

1.2.1 IO流分类

Java中的IO流可以按照数据的类型和流的方向进行分类:

  • 按数据类型分类
    • 字节流(Byte Stream):以字节为单位读写数据,适用于处理二进制数据,如图像、音频、视频等。常见的字节流类有InputStream和OutputStream。
    • 字符流(Character Stream):以字符为单位读写数据,适用于处理文本数据。字符流会自动进行字符编码和解码,可以处理多国语言字符。常见的字符流类有Reader和Writer。
  • 按流的方向分类
    • 输入流(Input Stream):用于读取数据。输入流从数据源读取数据,如文件、网络连接等。常见的输入流类有FileInputStream、ByteArrayInputStream、SocketInputStream等。
    • 输出流(Output Stream):用于写入数据。输出流将数据写入到目标地,如文件、数据库、网络等。常见的输出流类有FileOutputStream、ByteArrayOutputStream、SocketOutputStream等。

1.2.1.1 字符流
  • 只用来处理文本数据 ;
  • 数据最常见的表现形式是文件,字符流用来操作文件的子类一般是 FileReader 和 FileWriter 。

字符流读写文件注意事项:

  • 写入文件必须要用 flush() 刷新 ;
  • 用完流记得要关闭流 ;
  • 使用流对象要抛出IO异常 ;
  • 定义文件路径时,可以用"/"或者"\" ;
  • 在创建一个文件时,如果目录下有同名文件将被覆盖 ;
  • 在读取文件时,必须保证该文件已存在,否则抛出异常 ;
  • 字符流的缓冲区 ;
  • 缓冲区的出现是为了提高流的操作效率而出现的 ;
  • 需要被提高效率的流作为参数传递给缓冲区的构造函数 ;
  • 在缓冲区中封装了一个数组,存入数据后一次取出 。
1.2.1.2 字节流
  • 用来处理媒体数据 。

字节流读写文件注意事项:

  • 字节流和字符流的基本操作是相同的,但是想要操作媒体流就需要用到字节流 ;
  • 字节流因为操作的是字节,所以可以用来操作媒体文件(媒体文件也是以字节存储的);
  • 输入流(InputStream)、输出流(OutputStream);
  • 字节流操作可以不用刷新流操作 ;
  • InputStream特有方法:int available()(返回文件中的字节数);
  • 字节流的缓冲区 ;
  • 字节流缓冲区跟字符流缓冲区一样,也是为了提高效率 。

1.2.2 常用IO流

1.2.2.1 字节输入流类
  • InputStream:用于从输入源读取字节数据的抽象类。
  • FileInputStream:从文件中读取字节数据的类。
  • ByteArrayInputStream:从字节数组中读取字节数据的类。
  • BufferedInputStream:提供缓冲功能的字节输入流类。
  • DataInputStream:读取基本数据类型的字节输入流类。
try (InputStream is = new FileInputStream("input.txt");BufferedInputStream bis = new BufferedInputStream(is)) {int data;while ((data = bis.read()) != -1) {System.out.print((char) data);}
} catch (IOException e) {e.printStackTrace();
}
1.2.2.2 字符输入流类
  • Reader:用于从输入源读取字符数据的抽象类。
  • FileReader:从文件中读取字符数据的类。
  • BufferedReader:提供缓冲功能的字符输入流类。
  • InputStreamReader:将字节流转换为字符流的类。
try (Reader reader = new FileReader("input.txt");BufferedReader br = new BufferedReader(reader)) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}
} catch (IOException e) {e.printStackTrace();
}
1.2.2.3 字节输出流类
  • OutputStream:用于向输出目标写入字节数据的抽象类。
  • FileOutputStream:将字节数据写入文件的类。
  • ByteArrayOutputStream:将字节数据写入字节数组的类。
  • BufferedOutputStream:提供缓冲功能的字节输出流类。
  • DataOutputStream:将基本数据类型写入输出流的类。
try (OutputStream os = new FileOutputStream("output.txt");BufferedOutputStream bos = new BufferedOutputStream(os)) {String data = "Hello, World!";bos.write(data.getBytes());
} catch (IOException e) {e.printStackTrace();
}
1.2.2.4 字符输出流类
  • Writer:用于向输出目标写入字符数据的抽象类。
  • FileWriter:将字符数据写入文件的类。
  • BufferedWriter:提供缓冲功能的字符输出流类。
  • OutputStreamWriter:将字节流转换为字符流的类。
try (Writer writer = new FileWriter("output.txt");BufferedWriter bw = new BufferedWriter(writer)) {String data = "Hello, World!";bw.write(data);
} catch (IOException e) {e.printStackTrace();
}

1.2.3 Java Scanner类

Java 5添加了 java.util.Scanner 类,这是一个用于扫描输入文本的新的实用程序。

  1. 关于 nextInt()、next()、nextLine() 的理解 :
  • nextInt() :只能读取数值,若是格式不对,会抛出 java.util.InputMismatchException 异常 ;
  • next() :遇见第一个有效字符(非空格,非换行符)时,开始扫描,当遇见第一个分隔符或结束符(空格或换行符)时,结束扫描,获取扫描到的内容 ;
  • nextLine() :可以扫描到一行内容并作为字符串而被捕获到 。
  1. 关于 hasNext()、hasNextLine()、hasNextxxx() 的理解 :就是为了判断输入行中是否还存在xxx的意思。
  2. 与 delimiter() 有关的方法的理解 :应该是输入内容的分隔符设置,

二、BIO工作机制

客户端:

  • 通过Socket对象请求与服务端建立连接。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

服务端:

  • 通过ServerSocket注册端口。
  • 服务端通过调用accept方法用于监听客户端的Socket请求。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

三、简单编码实现

3.1 服务端

public class BioServer {public static void main(String[] args) {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8080);System.out.println("Server is listening on port 8080...");while (true) {// 阻塞等待客户端连接Socket clientSocket = serverSocket.accept();System.out.println("Client connected: " + clientSocket.getInetAddress());// 为每个客户端请求创建一个新线程进行处理new Thread(new ClientHandler(clientSocket)).start();}} catch (IOException e) {e.printStackTrace();} finally {try {if(serverSocket != null){serverSocket.close();}} catch (IOException e){e.printStackTrace();}}}
}class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {BufferedInputStream bufferedInputStream = null;BufferedOutputStream bufferedOutputStream  = null;try {bufferedInputStream = new BufferedInputStream(socket.getInputStream());;bufferedOutputStream  = new BufferedOutputStream(socket.getOutputStream());System.out.println("收到来自客户端的消息:");byte[] bytes = new byte[1024]; //创建字节数组,存储临时读取的数据int len ; //记录数据读取的长度if ((len = bufferedInputStream.read(bytes)) > -1) { //长度为-1则读取完毕String result = new String(bytes,0,len);System.out.println(result);}String outString = "服务端收到,这里是线程" + Thread.currentThread().getName();System.out.println("回复信息给客户端: " + outString);bufferedOutputStream.write(outString.getBytes());bufferedOutputStream.flush();System.out.println("回复完成========");} catch (IOException e) {e.printStackTrace();} finally {System.out.println("关闭数据流========");try {if (bufferedInputStream != null) {bufferedInputStream.close();}if (bufferedOutputStream != null) {bufferedOutputStream.close();}} catch (IOException e){e.printStackTrace();}}}
}

工作过程:

  • 服务器通过 ServerSocket 对象监听 8080 端口,等待客户端连接。
  • 每当有一个客户端连接到服务器时,serverSocket.accept() 会阻塞当前线程,直到有客户端连接进来。
  • 然后,服务器会为每个客户端连接创建一个新线程来处理该客户端的请求。
  • 线程通过 BufferedInputStream 读取客户端发送的数据,并通过 BufferedOutputStream 回复客户端。

3.2 客户端

public class BioClient {public static void start() throws IOException {Socket socket = new Socket("127.0.0.1", 8080);String msg = "Hi,This is the BioClient";byte[] msgBytes = msg.getBytes();//1.拿到输出流//2.对输出流进行处理BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());//3.输出msgbufferedOutputStream.write(msgBytes);bufferedOutputStream.flush();System.out.println("客户端发送消息完毕: " + msg);System.out.println("客户端开始接收到消息==========");//4.对输入流进行处理BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());System.out.println("客户端接收到的消息:");byte[] inBytes = new byte[1024];int len;//5.读取输入流if ((len = bufferedInputStream.read(inBytes)) != -1) {String result = new String(inBytes, 0, len);System.out.println(result);}//6.关闭输入输出流bufferedOutputStream.close();bufferedInputStream.close();socket.close();}public static void main(String[] args) throws IOException {start();}
}

工作过程:

  • 与服务端建立连接
  • 发送消息给服务端
  • 接收服务端返回的消息

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

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

相关文章

【ArcGIS_Python】使用arcpy脚本将shape数据转换为三维白膜数据

说明: 该专栏之前的文章中python脚本使用的是ArcMap10.6自带的arcpy(好几年前的文章),从本篇开始使用的是ArcGIS Pro 3.3.2版本自带的arcpy,需要注意不同版本对应的arcpy函数是存在差异的 数据准备:准备一…

【电脑系统】电脑突然(蓝屏)卡死发出刺耳声音

文章目录 前言问题描述软件解决方案尝试硬件解决方案尝试参考文献 前言 在 更换硬盘 时遇到的问题,有时候只有卡死没有蓝屏 问题描述 更换硬盘后,电脑用一会就卡死,蓝屏,显示蓝屏代码 UNEXPECTED_STORE_EXCEPTION 软件解决方案…

基于LabVIEW的Modbus-RTU设备通信失败问题分析与解决

在使用 LabVIEW 通过 Modbus-RTU 协议与工业设备进行通信时,可能遇到无法正常发送或接收指令的问题。常见原因包括协议参数配置错误、硬件连接问题、数据帧格式不正确等。本文以某 RGBW 控制器调光失败为例,提出了一种通用的排查思路,帮助开发…

解决Mac安装软件的“已损坏,无法打开。 您应该将它移到废纸篓”问题

mac安装软件时,如果出现这个问题,其实很简单 首先打开终端,输入下面的命令 sudo xattr -r -d com.apple.quarantine 输入完成后,先不要回车,点击访达--应用程序--找到你无法打开的app图标,拖到终端窗口中…

1.攻防世界easyphp

进入题目页面如下 是一段PHP代码进行代码审计 <?php // 高亮显示PHP文件源代码 highlight_file(__FILE__);// 初始化变量$key1和$key2为0 $key1 0; $key2 0;// 从GET请求中获取参数a的值&#xff0c;并赋值给变量$a $a $_GET[a]; // 从GET请求中获取参数b的值&#xff…

牛客周赛 Round 79

题目目录 A 小红的合数寻找解题思路参考代码 B 小红的小球染色解题思路参考代码 C 小红的二叉树解题思路参考代码 D 小红的“质数”寻找解题思路参考代码 E 小红的好排列解题思路参考代码 F 小红的小球染色期望解题思路参考代码 A 小红的合数寻找 \hspace{15pt} 小红拿到了一个…

《苍穹外卖》项目学习记录-Day11订单统计

根据起始时间和结束时间&#xff0c;先把begin放入集合中用while循环当begin不等于end的时候&#xff0c;让begin加一天&#xff0c;这样就把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间&#xff08;0点0分0秒&#xff09;小于…

电子电器架构 --- 电子电气架构设计要求与发展方向

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

DeepSeek 的含金量还在上升

大家好啊&#xff0c;我是董董灿。 最近 DeepSeek 越来越火了。 网上有很多针对 DeepSeek 的推理测评&#xff0c;除此之外&#xff0c;也有很多人从技术的角度来探讨 DeepSeek 带给行业的影响。 比如今天就看到了一篇文章&#xff0c;探讨 DeepSeek 在使用 GPU 进行模型训练…

Fastdds学习分享_xtpes_发布订阅模式及rpc模式

在之前的博客中我们介绍了dds的大致功能&#xff0c;与组成结构。本篇博文主要介绍的是xtypes.分为理论和实际运用两部分.理论主要用于梳理hzy大佬的知识&#xff0c;对于某些一带而过的部分作出更为详细的阐释&#xff0c;并在之后通过实际案例便于理解。案例分为普通发布订阅…

OpenGL学习笔记(五):Textures 纹理

文章目录 纹理坐标纹理环绕方式纹理过滤——处理纹理分辨率低的情况多级渐远纹理Mipmap——处理纹理分辨率高的情况加载与创建纹理 &#xff08; <stb_image.h> &#xff09;生成纹理应用纹理纹理单元练习1练习2练习3练习4 通过上一篇着色部分的学习&#xff0c;我们可以…

unity学习26:用Input接口去监测: 鼠标,键盘,虚拟轴,虚拟按键

目录 1 用Input接口去监测&#xff1a;鼠标&#xff0c;键盘&#xff0c;虚拟轴&#xff0c;虚拟按键 2 鼠标 MouseButton 事件 2.1 鼠标的基本操作 2.2 测试代码 2.3 测试情况 3 键盘Key事件 3.1 键盘的枚举方式 3.2 测试代码同上 3.3 测试代码同上 3.4 测试结果 4…

Flink2支持提交StreamGraph到Flink集群

最近研究Flink源码的时候&#xff0c;发现Flink已经支持提交StreamGraph到集群了&#xff0c;替换掉了原来的提交JobGraph。 新增ExecutionPlan接口&#xff0c;将JobGraph和StreamGraph作为实现。 Flink集群Dispatcher也进行了修改&#xff0c;从JobGraph改成了接口Executio…

AJAX笔记进阶篇

黑马程序员视频地址&#xff1a; AJAX-Day04-01.同步代码和异步代码https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p47https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2…

利用Muduo库实现简单且健壮的Echo服务器

一、muduo网络库主要提供了两个类&#xff1a; TcpServer&#xff1a;用于编写服务器程序 TcpClient&#xff1a;用于编写客户端程序 二、三个重要的链接库&#xff1a; libmuduo_net、libmuduo_base、libpthread 三、muduo库底层就是epoll线程池&#xff0c;其好处是…

【学术投稿-2025年计算机视觉研究进展与应用国际学术会议 (ACVRA 2025)】从计算机基础到HTML开发:Web开发的第一步

会议官网&#xff1a;www.acvra.org 简介 2025年计算机视觉研究进展与应用&#xff08;ACVRA 2025&#xff09;将于2025年2月28-3月2日在中国广州召开&#xff0c;将汇聚世界各地的顶尖学者、研究人员和行业专家&#xff0c;聚焦计算机视觉领域的最新研究动态与应用成就。本次…

Rust 所有权特性详解

Rust 所有权特性详解 Rust 的所有权系统是其内存安全的核心机制之一。通过所有权规则&#xff0c;Rust 在编译时避免了常见的内存错误&#xff08;如空指针、数据竞争等&#xff09;。本文将从堆内存与栈内存、所有权规则、变量作用域、String 类型、内存分配、所有权移动、Cl…

【Git】一、初识Git Git基本操作详解

文章目录 学习目标Ⅰ. 初始 Git&#x1f4a5;注意事项 Ⅱ. Git 安装Linux-centos安装Git Ⅲ. Git基本操作一、创建git本地仓库 -- git init二、配置 Git -- git config三、认识工作区、暂存区、版本库① 工作区② 暂存区③ 版本库④ 三者的关系 四、添加、提交更改、查看提交日…

【Envi遥感图像处理】009:envi5.6设置中文界面的方法

ENVI软件从5.0版本开始,界面发生了大的变化,并开始支持中文。本文讲述envi5.6设置中文界面的方法。 文章目录 一、中文界面预览二、设置中文界面三、注意事项一、中文界面预览 以下为envi5.6新版的中文界面: 二、设置中文界面 打开英文版的envi5.6软件,首先需要从安装App…

014-STM32单片机实现矩阵薄膜键盘设计

1.功能说明 本设计主要是利用STM32驱动矩阵薄膜键盘&#xff0c;当按下按键后OLED显示屏上会对应显示当前的按键键值&#xff0c;可以将此设计扩展做成电子秤、超市收银机、计算器等需要多个按键操作的单片机应用。 2.硬件接线 模块管脚STM32单片机管脚矩阵键盘行1PA0矩阵键盘…