一步一步走来,之前去学习了JUC并发编程知识,现在终于到Java IO网络编程啦,难啊。
引入: 随着技术的发展,两个或以上的程序必然需要进行交互,于是提供了一种端到端的通信,相当于对传输层的一种封装,对于开发人员而言隐藏了传输的细节,将这些固定的“套路”抽象出来,提供一种端到端的通信,可以使我们更加专注于业务的开发。而BIO只是其中一种。
Java BIO
(old )就是传统的 Java I/O
编程,其相关的类和接口在 java.io
,另外Java BIO是同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理。效率较低,资源容易被浪费。
阻塞和非阻塞:
阻塞和非阻塞指的是执行一个操作是等操作结束再返回,还是马上返回。
比如餐馆的服务员为用户点菜,当有用户点完菜后,服务员将菜单给后台厨师,此时有两种方式:
流程分析:
ServerSocket
服务端,用来供客户端连接我们使用BIO模型写一个服务器端,监听8888端口,当有客户端连接时,就启动一个线程与它通讯。
编程思路:
ServerSocket
对象 服务器套接字(ServerSocket)等待通过网络传入的请求。Socket
对象, 用于连接通信handler
方法,和客户端通讯,读取客户端发过来的信息package com.crush.bio;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
/**
* @Author: crush
* @Date: 2021-08-23 11:51
* version 1.0c
*/
public class BioServer {
public static void main(String[] args) throws Exception {
//1. 创建一个线程池
ExecutorService newCachedThreadPool = new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//2、创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器启动了");
while (true) {
System.out.println("线程信息id = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());
//监听,等待客户端连接
System.out.println("等待连接....");
//3.侦听要与此套接字建立的连接并接受它。 该方法阻塞,直到建立连接。
final Socket socket = serverSocket.accept();
System.out.println("连接到一个客户端");
//4、就创建一个线程,与之通讯(单独写一个方法)
newCachedThreadPool.execute(() -> {
//可以和客户端通讯
handler(socket);
});
}
}
/**
* 编写一个handler方法,和客户端通讯,读取客户端发过来的信息
* @param socket
*/
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();
}
}
}
}
这是一个经典的每连接每线程的模型,之所以使用多线程,主要原因在于socket.accept()、socket.read()、socket.write()
三个主要函数都是同步阻塞的,当一个连接在处理I/O的时候,系统是阻塞的,如果是单线程的话必然就挂死在那里;但CPU是被释放出来的,开启多线程,就可以让CPU去处理更多的事情。
telnet localhost 8888
CTRL+]
send 信息
客户端:
客户端我也有写的哈😁
package com.crush.bio;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class BIOEchoClient {
public static void main(String[] args) throws Exception{
Socket client = new Socket("localhost",8888);
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while (flag){
Scanner scanner = new Scanner(System.in);
String inputData = scanner.nextLine().trim();
out.println(inputData);
if ("byebye".equalsIgnoreCase(inputData)){
flag = false;
System.out.println("和客户端说再见拉!!!");
}
}
client.close();
}
}
这个测试就不说了哈,这个蛮简单的。
我们使用telnet
命令来测试,默认Windows这个命令是关闭的,就会出现和我一样的问题。
打开方式:打开控制面板,点程序,然后再点这个进行选择。
BIO的最大缺陷就是在于每个请求都需要创建独立的线程进行连接通讯,这样会造成以下几点问题:
最近在持续更新中,如果你觉得对你有所帮助,也感兴趣的话,关注我吧,让我们一起学习,一起讨论吧。
在学习路上充满好奇心,明白思考的重要性,是支持我一直学习下去的积极推动力吧。希望你也能喜欢上编程!😁
热爱生活,享受生活!!!无论在哪里,无论此时的生活多么艰难,都要记得热爱生活!!!相信总会有光来的。
你好,我是博主宁在春
,Java学习路上的一颗小小的种子,也希望有一天能扎根长成苍天大树。
希望与君共勉
😁
我们:待别时相见时,都已有所成。