二、JAVA BIO

article/2025/11/8 10:15:34

NIO 目录



二、JAVA BIO

1、 Java BIO基本介绍

Java BlO就是传统的Java IO编程,其相关的类和接口在Java.io 包中。

BIO(blocking I/O)同步阻塞,服务器实现模式为:一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善 (实现多个客户连接服务器)。

2、 java BIO工作机制

3、传统的BIO编程实例回顾

网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信(绑定IP地址和端口),客户端通过连接操作向服务端监听的端口地址发起连接请求,基于TCP协议下进行三次握手连接,连接成功后,双方通过网络套接字(Socket)进行通信。

传统的同步阻塞模型开发中,服务端ServerSocket负责绑定IP地址,启动监听端口;客户端Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。

基于BIO模式下的通信,客户端-服务端是完全同步,完全藕合的。

3.1、客户端案例如下

package bio;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;/*** 客户端*/
public class Client {public static void main(String[] args) {try {//1、创建socket链接Socket socket = new Socket("127.0.0.1", 9999);//2、从socket对象中获取一个输出流OutputStream out = socket.getOutputStream();//3、把字节输出流包装成打印流PrintStream printStream = new PrintStream(out);printStream.println("hello World! 服务端,你好");printStream.flush();} catch (IOException e) {e.printStackTrace();}}
}

3.2、服务端案例如下

package bio;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*** 目标:客户端发送消息,服务端接收消息*/
public class Server {public static void main(String[] args) {try {System.out.println("===服务端启动===");//1、对服务端端口进行注册 9999ServerSocket serverSocket = new ServerSocket(9999);//2、监听客户端socket请求Socket socket = serverSocket.accept();//3、从socket管道中得到一个字节输入流对象InputStream is = socket.getInputStream();//4、将字节输入流包装成一个缓冲字符输入流,提高效率BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg;/*while ((msg = br.readLine()) != null){System.out.println("服务端接收到:"+msg);}*/if ((msg = br.readLine()) != null){System.out.println("服务端接收到:" + msg);}} catch (IOException e) {e.printStackTrace();}}
}

3.3、输出

===服务端启动===
服务端接收到:hello World! 服务端,你好

3.4、小结

在以上通信中,服务端会一直等待客户端的消息,如果客户端没有进行消息的发送,服务端将一直进入阻塞状态。

同时服务端是按照行获取消息的,这意味着客户端也必须按照行进行消息的发送,否则服务端将进入等待消息的阻塞状态!

4、BIO模式下多发和多收消息

在上面的案例中,只能实现客户端发送消息,服务端接收消息, 并不能实现反复的收消息和反复的发消息,我们只需要在客户端案例中,加上反复按照行发送消息的逻辑即可! 案例代码如下:

4.1、客户端代码如下

package bio;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;/*** 客户端*/
public class Client {public static void main(String[] args) {try {//1、创建socket链接Socket socket = new Socket("127.0.0.1",9999);//2、从socket对象中获取一个输出流OutputStream out = socket.getOutputStream();//3、把字节输出流包装成打印流PrintStream printStream = new PrintStream(out);Scanner sc = new Scanner(System.in);while (true){System.out.print("请说:");String msg = sc.nextLine();printStream.println(msg);printStream.flush();}} catch (IOException e) {e.printStackTrace();}}
}

4.2、服务端代码如下

package bio;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*** 目标:服务端可以反复的接收消息,客户端可以反复的发送消息*/
public class Server {public static void main(String[] args) {try {System.out.println("===服务端启动===");//1、对服务端端口进行注册 9999ServerSocket serverSocket = new ServerSocket(9999);//2、监听客户端socket请求Socket socket = serverSocket.accept();//3、从socket管道中得到一个字节输入流对象InputStream is = socket.getInputStream();//4、将字节输入流包装成一个缓冲字符输入流,提高效率BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg;while ((msg = br.readLine()) != null){System.out.println("服务端接收到:"+msg);}} catch (IOException e) {e.printStackTrace();}}
}

4.3、输出

客户端:
请说:hello
请说:what are you doing?
请说:服务端:
===服务端启动===
服务端接收到:hello
服务端接收到:what are you doing?

5、BIO模式下接收多个客户端

5.1、概述

在上述的案例中,一个服务端只能接收一个客户端的通信请求,那么如果服务端需要处理很多个客户端的消 息通信请求应该如何处理呢,此时我们就需要在服务端引入线程了,也就是说客户端每发起一个请求,服务端就创建一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程的模型。

图解模式如下:

1-1

5.2、客户端案例代码

package bio;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;/*** 客户端*/
public class Client {public static void main(String[] args) {try {//1、创建socket链接Socket socket = new Socket("127.0.0.1",9999);//2、从socket对象中获取一个输出流OutputStream out = socket.getOutputStream();//3、把字节输出流包装成打印流PrintStream printStream = new PrintStream(out);Scanner sc = new Scanner(System.in);while (true){System.out.print("请说:");String msg = sc.nextLine();printStream.println(msg);printStream.flush();}} catch (IOException e) {e.printStackTrace();}}
}

5.3、服务端案例代码

package bio;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*** 目标:服务端可以实现同时接收多个客户端的Socket通信需求* 思路:服务端每接收到一个客户端socket请求对象之后都交给一个独立的线程来处理客户端的数据交互需求*/
public class Server {public static void main(String[] args) {try {//1.注册端口ServerSocket ss = new ServerSocket(9999);//2.定义一个死循环,负责不断的接收客户端的Socket的连接请求while(true){Socket socket = ss.accept();//3.创建一个独立的线程来处理与这个客户端的socket通信需求new ServerThreadReader(socket).start();}} catch (IOException e) {e.printStackTrace();}}
}

5.4、线程类

public class ServerThreadReader extends Thread {private Socket socket;public ServerThreadReader(Socket socket){this.socket = socket;}@Overridepublic void run() {try {//1、从socket对象中得到一个字节输入流InputStream is = socket.getInputStream();//2、使用缓存字符输入流包装字节输入流BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg;while ((msg = br.readLine()) != null){System.out.println(msg);}} catch (IOException e) {e.printStackTrace();}}
}

5.5、输出

===client1:
请说:
ppp
请说:
你在干嘛?
请说:
我是第一个client
请说:===client2:
请说:
lll
请说:
还钱!!!
请说:
我是第二个client
请说:===client3:
请说:
我是第三个client
请说:===server:
lll
ppp
你在干嘛?
还钱!!!
我是第二个client
我是第一个client
我是第三个client

5.6、小结

  • 每个Socket接收到,都会创建一个线程,线程的竞争、切换上下文影响性能;
  • 每个线程都会占用栈空间和CPU资源;
  • 并不是每个socket都进行lO操作,无意义的线程处理;
  • 客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出,
  • 线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。

6、伪异步I/O编程

6.1、概述

在上述案例中:客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出,线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。

接下来我们采用一个伪异步I/O的通信框架,采用线程池和任务队列实现,当客户端接入时,将客户端的Socket封装成一个Task (该任务实现Java. lang. Runnable(线程任务接口)交给后端的线程池中进行处理。JDK的线程池维护一个消息队列和N个活跃的线程,对消息队列中Socket任务进行处理,由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

如下图所示:

6.2、客户端案例代码

package bio;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;/*** 客户端*/
public class Client {public static void main(String[] args) {try {//1、创建socket链接Socket socket = new Socket("127.0.0.1",9999);//2、从socket对象中获取一个输出流OutputStream out = socket.getOutputStream();//3、把字节输出流包装成打印流PrintStream printStream = new PrintStream(out);Scanner sc = new Scanner(System.in);while (true){System.out.print("请说:");String msg = sc.nextLine();printStream.println(msg);printStream.flush();}} catch (IOException e) {e.printStackTrace();}}
}

6.3、服务端案例代码

package bio;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** 目标:开发实现伪异步通讯架构* 思路:服务端没接收到一个客户端socket请求对象之后都交给一个独立的线程来处理客户端的数据交互需求*/
public class Server {public static void main(String[] args) {try {//1.注册端口ServerSocket ss = new ServerSocket(9999);//2.定义一个死循环,负责不断的接收客户端的Socket的连接请求//初始化一个线程池对象HandlerSocketServerPool pool = new HandlerSocketServerPool(3,10);while(true){Socket socket = ss.accept();//3.把socket对象交给一个线程池进行处理// 把socket封装成一个任务对象交给线程池处理Runnable target = new ServerRunnableTarget(socket);pool.execute(target);}} catch (IOException e) {e.printStackTrace();}}
}

6.4、线程池处理类

public class HandlerSocketServerPool {//1. 创建一个线程池的成员变量用于存储一个线程池对象private ExecutorService executorService;/*** 2.创建这个类的的对象的时候就需要初始化线程池对象* public ThreadPoolExecutor(int corePoolSize,* int maximumPoolSize,* long keepAliveTime,* TimeUnit unit,* BlockingQueue<Runnable> workQueue)*/public HandlerSocketServerPool(int maxThreadNum, int queueSize){executorService = new ThreadPoolExecutor(3, maxThreadNum, 120, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));}/*** 3.提供一个方法来提交任务给线程池的任务队列来暂存,等待线程池来处理*/public void execute(Runnable target){executorService.execute(target);}
}

6.5、Socket任务类

public class ServerRunnableTarget implements Runnable {private Socket socket;public ServerRunnableTarget(Socket socket){this.socket = socket;}@Overridepublic void run() {//处理接收到的客户端socket通信需求try {//1.从socket管道中得到一个字节输入流对象InputStream is = socket.getInputStream();//2.把字节输入流包装成一个缓存字符输入流BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg;while((msg = br.readLine()) != null){System.out.println("服务端收到:" + msg);}} catch (Exception e){e.printStackTrace();}}
}

6.5、输出

服务端收到:client1
服务端收到:client2
服务端收到:client3
服务端收到:client4
java.net.SocketException: Connection resetat java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)at java.base/java.io.BufferedReader.fill(BufferedReader.java:161)at java.base/java.io.BufferedReader.readLine(BufferedReader.java:326)at java.base/java.io.BufferedReader.readLine(BufferedReader.java:392)at com.zhangxudong.ServerRunnableTarget.run(ServerRunnableTarget.java:23)atjava.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)atjava.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)at java.base/java.lang.Thread.run(Thread.java:834)
服务端收到:client5

6.6、小结

  • 伪异步旧采用了线程池实现,因此避免了为每个请求创建一个独立线程造成线程资源耗尽的问题,但由于底层 依然是采用的同步阻塞模型,因此无法从根采上解决问题。
  • 如果单个消息处理的缓慢,或者服务器线程池中的全部线程都被阻塞,那么后续socket的I/O消息都将在队列 中排队。新的Socket请求将被拒绝,客户端会发生大量连接超时。

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

相关文章

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…

QuartusII中LPM_COUNTER的使用

ALTERA建议&#xff0c;在设计时时序允许的情况下尽量使用Megafunction的资源&#xff0c;因为在多数情况下Megafunction的综合和实现结果更为优化。现在&#xff0c;就LPM_COUNTER的使用&#xff0c;浅谈一下。 Megafunction中LPM_COUNTER的参数设定主要是以下三部分&#xf…

quartus II 18.1 下载

quartus II 18.1 下载链接 以及解析 链接:https://pan.baidu.com/s/1warS-Vvv1maDmOKu8RsteQ 提取码&#xff1a;awxd 这个链接是已经下好的安装包 链接:https://pan.baidu.com/s/13HuyxUZvZ19vdYUmlLJujQ 提取码&#xff1a;gudn 第二个链接解压密码&#xff1a; wqlx.13542…

Quartus II14.1安装教程

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

quartus ii matlab,基於Quartus II和MATLAB的FIR濾波器設計與仿真(二)

接上文 基於Quartus II和MATLAB的FIR濾波器設計與仿真(一)&#xff1a; 3 QuartusII 調用 IP 核生成 FIR 濾波器模塊 在 Quartus II 中&#xff0c; Altera 提供了一系列可供用戶免費使用的 IP 核&#xff0c; FIR濾波器就包含其中&#xff0c;所以只需要在 Quartus II 中調用…

安装Quartus II教程

下载Quartus安装包&#xff0c;给大家一个11.3版本的安装包 链接&#xff1a;https://pan.baidu.com/s/1eXtjL2JZVGV1RBC0VozqVQ?pwdhmnv 提取码&#xff1a;hmnv 1.打开安装程序&#xff0c;点击next 2.点击接受&#xff0c;下一步 3.选择安装路径&#xff0c;这里最好选择…

Quartus II 仿真

Quartus II 使用university program VWF仿真 1.File->new->university program VWF->OK打开仿真页面 2.edit->insert->insert node or bus或者直接双击左边空白地方弹出insert node or bus对话框。 3.node finder->list-> >> ->OK->OK 4.设…

QuartusII9.0--项目文件的新建

第一步&#xff1a;打开QuartusII软件&#xff0c;界面如下&#xff1a; 第二步&#xff1a;选择File->New Project Wizard菜单项&#xff0c;则弹出New Project Wizard:Indroduction对话框&#xff0c;如下图所示&#xff1a; 单击Next按钮&#xff0c;则进入项目工程的目…

Quartus II报错

使用如下电路语句创建异步时序实现D触发器时 一直报错 Error (10200): Verilog HDL Conditional Statement error at flip_flop.v(9): cannot match operand(s) in the condition to the corresponding edges in the enclosing event control of the always construct 在网上…

【QuartusII】0-创建工程模板

一、创建工程 1、激活安装quartus II软件后&#xff0c;打开即见如下界面 2、在菜单栏 “File -> New Project Wizard…”中&#xff0c;进入创建工程流程 3、第一部分&#xff0c;如下图&#xff0c;配置路径、项目名称、以及顶层文件&#xff08;类似C语言的main&#xf…

quartus II 18.1 Qsys简单操作步骤

1.建立工程 2.选择芯片时&#xff0c;在界面device and pin options中的unused pins选择as input tri-stated ; 3.创建NiOS II软核处理系统 &#xff0c;18.1在tools->platform designer 4.出现该界面&#xff0c;选择file->save->nios2_small 保存成功后找到下面nio…