文章目录
Java IO 的演变之路
I/O模型:就是用什么样的通道或者说是通信模式和架构进行数据的传输和接受,很大程度上决定了程序通信的性能,在Java 当中一种支持 3 种 IO模型。
BIO、NIO、AIO
在实际通信需求下,要根据不同的业务场景和性能需求决定选择不同的 IO 模型。
I/O 模型
Java BIO:同步并阻塞的(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有链接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情就会造成不必要的线程开销。
BIO 方式主要适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的唯一选择,但程序方面简单易理解。
Java NIO:同步非阻塞。服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求就进行处理。
NIO 方式主要适用于连接数目比较多且连接数据比较短(轻操作)的架构。比如:聊天服务器,弹幕系统,服务器间通信,JDK 1.4 开始支持这方面的事了。
Java AIO:异步非阻塞(NIO.2),服务器实现模式为一个有效情趣一个线程,客户端的 I/O 请求都是由 OS 先完成了再通知服务器应用去启动线程进行处理,一般适用于连接数目较多并连接时间较长的应用。
AIO 方式主要适用于连接数目多且连接数据比较长(重操作)的结构。比如:相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK 7天之后开始支持。
基本介绍以及工作机制
Java BIO 就是传统的 Java IO 编程,其相关的类型和接口是在 java.io 包中。
BIO(blocking I/O)同步阻塞,服务器实现模式为一个连接一个线程,即客户端游连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情就会造成不必要的线程开销,可以通过线程池机制来进行改善(实现多个客户连接服务器)
网络编程的基本模型是什么 Client/Server 模型,也就是两个进程之间进行相互的通信,其中服务端提供位置信息(绑定 IP 地址和端口),客户端通过连接操作向服务端监听的端口地址发起连接请求,基于 TCP 协议下进行三次握手连接,连接成功后,双方通过网络套接字(Socket)进行通信。
传统的同步阻塞模型还在开发中,服务器端 ServerSocket 负责绑定 IP 地址,启动监听端口;客户端 Socket 负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
基于 BIO 模式下的通信,客户端-服务端是完全同步的,完全耦合的。
BIO 模式下多发和多收模式
代码演示,代码说明一切
服务端:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端
* 可以反复的接收消息,客户端可以反复的发送消息
*/
public class Server {
public static void main(String[] args) {
try {
System.out.println("===服务端启动===");
//1.定义一个ServerSocket对象进行服务端的端口注册
ServerSocket ss = new ServerSocket(9999);
//2. 监听客户端的Socket连接请求
Socket socket = ss.accept();
//3.从socket管道中得到一个字节输入流对象
InputStream inputStream = socket.getInputStream();
//4.把字节输入改为字符输入流
InputStreamReader reader = new InputStreamReader(inputStream);
//4.把字符输入流包装成一个缓存字符输入流
BufferedReader br = new BufferedReader(reader);
String msg;
while ((msg = br.readLine()) != null) {
System.out.println("服务端接收到:" + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端:
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 客户端
* 用来发送数据
*/
public class Client {
public static void main(String[] args) {
try {
//1.创建Socket对象请求服务端的连接
Socket socket = new Socket("127.0.0.1",9999);
//2.从Socket对象中获取一个字节输出流
OutputStream os = socket.getOutputStream();
//3.把字节输出流包装成一个打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("请说:");
String msg = sc.nextLine();
ps.println(msg);
ps.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在上面的通信当中,服务端会一直等待客户端的消息,如果客户端没有进行消息的发送,服务端将会一直处于阻塞状态。
同时服务端是按照行获取信息的,这就意味着客户端也必须按照进行消息的发送,否则服务端将进入等待消息的阻塞状态。
同时在这,若先关闭客户端,那么服务端就将会报错。Connection reset(连接重置的意思)
BIO 模式下接受多个客户端
上面的是一个服务端只能结束一个客户端的通信请求,那么如果服务端需要很多个客户端的信息通信请求应该如何处理,在这个时候我们就可以引入线程,也就是说客户端没发起一个请求,服务端就创建一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程的模型了。
代码说明:
服务端代码:
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端
* 接受客户端发来的请求
* 因为服务端要求接受多个客户端的连接,所以以现场操作进行
*/
public class Server {
public static void main(String[] args) {
try {
System.out.println("===服务端启动===");
//1.注册端口
ServerSocket ss = new ServerSocket(9999);
//2.定义一个死循环,负责不断的接收客户端的Socket的连接请求
while (true) {
// 阻塞式的,当遇到一个客户端请求连接,通过,当没有的时候,阻塞
Socket socket = ss.accept();
//3.创建一个独立的线程来处理与这个客户端的socket通信的具体需求
new ServerThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端需要接受多个客户端,就要开启多线程。多线程代码:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 服务端创建的线程,服务端后执行的东西主要就是在这进行执行
*/
public class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// 在这执行具体的客户端的具体操作
try {
//从 socket 对象中得到字节的输入流