首先要了解 阻塞非阻塞 同步异步
同步异步:
同步:是指发发送的一个消息 一个请求 需要对面返回 一个消息或者一个状态! 如果没有返回需要一直等待返回
异步:异步调用请求发送后,调用者无需等待消息返回便可进行其他操作,消息会以通知的方式告知调用者 就比如 你发送个微信 不需要立马回复 等一段时间 或者 等对方心情好在回复(添狗)
阻塞非阻塞:
阻塞:当前消息请求发送 没有结构返回 那么 当前线程会被挂起 直至返回消息
非阻塞:不能立刻得到结果之前,该调用不会阻塞当前线程!想干别的还可以干
BIO:
BIO(Blocking I/O) 同步阻塞IO
JDK1.4之前唯一选择, 只有这一种IO模型
问题:
有多个客户端进行连接时, 如: client1, client2 进行连接, 由于client1先与服务端进行了连接, 此时服务端只能处理client1的请求, 什么时候client1处理完, 什么时候才能处理其他的请求, 服务端的线程一直处理client1的请求, 所以再有其他的请求, 是没有办法处理的TCP/IP底层使用的是BIO, 同步阻塞的IO, 发起请求后, 必须等待接收到响应之后, 代码才能继续向下执行, 并且服务端一个线程只能处理一个客户端的一个请求!
解决办法 ;
package com.mcc.bio;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author mcc
* @title: MyServer
* @projectName WEAVER
* @description: TODO
* @date 2022/5/1116:36
*/
public class MyServer {
public static void main(String[] args) {
try {
//1.创建ServerSocket, 自动监听指定的ip+port
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("等待客户端连接");
for(;;){
//2.阻塞等待客户端的连接
Socket accept = serverSocket.accept();
System.out.println("客户端连接了");
System.out.println("等待接受客户端的消息");
/* 一个客户端的请求, 服务端需要创建一个线程提供服务 */
new Thread(() -> {
for(;;){
//3.同步阻塞接受客户端的消息
try {
InputStream inputStream = accept.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
System.out.println("接收到客户端消息: "+dataInputStream.readUTF());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.mcc.bio;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @author mcc
* @title: MyClient1
* @projectName WEAVER
* @description: TODO
* @date 2022/5/1116:38
*/
public class MyClient1 {
public static void main(String[] args) {
try {
//1.创建客户端Socket, 自动连接指定的ip+port
Socket socket = new Socket("127.0.0.1", 9999);
Scanner scanner = new Scanner(System.in);
System.out.println("向服务端发送消息");
for(;;){
//向服务端输出
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
String next = scanner.next();
dataOutputStream.writeUTF("客户端1"+next);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
每个客户端需要对应服务端的一个子线程。 所以有多少个客户端, 服务端就需要创建多少个子线程。如果客户端特别多,几万甚至几百万,服务器端就需要有几万甚至几百万的子线程。 由于每个子线程都有自己的独立线程区,这么多子线程可能就产生内存不足等问题。而且这么多的子线程也需要进行调度, 切换, 销毁这也是非常消耗性能的
虽然可以解决一个服务端处理多个客户端,但是因为过多的子线程导致系统资源消耗过多,线程切换导致性能下降也是我们不得不需要考虑的问题
可以使用线程池,在一定程度上提升程序性能
NIO:
NIO称为New IO(新的IO)又称Non-Blocking IO(非阻塞IO)
NIO的特性就是同步非阻塞IO模型
优势:
使用nio解决了使用BIO服务端创建过多子线程产生内存不足, 这么多的子线程也需要进行调度, 切换, 销毁也是非常消耗性能的问题
问题解决:
package com.mcc.bio;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* @author mcc
* @title: MyNioServer
* @projectName WEAVER
* @description: TODO
* @date 2022/5/1116:49
*/
public class MyNioServer {
public static void main(String[] args) {
try {
//1.开启ServerSocketChannel管道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.设置为非阻塞
serverSocketChannel.configureBlocking(false);
//3.绑定ip与port
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 9999));
//4.将ServerSocketChannel注册到Selector选择器中
/*
* 1.创建选择器
* 2.监听管道的状态(服务端等待接受客户端的连接状态)
* */
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
//5.获取管道的状态, 没有状态阻塞
selector.select();
//6.获取所有事件的迭代器
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
//7.获取迭代器中的每一个状态
SelectionKey next = iterator.next();
//8.状态为成功接收到客户端时
if(next.isAcceptable()){
//9.接收客户端的连接
SocketChannel accept = serverSocketChannel.accept();
System.out.println("接收到了客户端的连接");
//10.设置为非阻塞
accept.configureBlocking(false);
//11.监听数据的读取状态(接收到消息时, 为该状态)
accept.register(selector, SelectionKey.OP_READ);
}
if(next.isReadable()){
/* 接收客户端消息 */
//12.获取客户端管道
SocketChannel socketChannel = (SocketChannel) next.channel();
//13.读取客户端发送的消息
ByteBuffer allocate = ByteBuffer.allocate(1024);
socketChannel.read(allocate);
byte[] array = allocate.array();
System.out.println(new String(array, 0, allocate.position()));
/* 向客户端发送的消息 */
socketChannel.write(ByteBuffer.wrap("在吗?".getBytes()));
}
//移除当前的事件防止重复处理
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.mcc.bio;
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;
/**
* @author mcc
* @title: MyNioClient
* @projectName WEAVER
* @description: TODO
* @date 2022/5/1116:53
*/
public class MyNioClient {
public static void main(String[] args) throws IOException {
try {
//1.开启SocketChannel管道
SocketChannel socketChannel = SocketChannel.open();
//2.设置为非阻塞
socketChannel.configureBlocking(false);
//3.将管道注册到Selector(选择器)中
/*
* 1.参数一: 选择器
* 2.参数二: 监听已经与服务端连接的状态
* */
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//4.连接服务端
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
while(true) {
//5.获取客户端的状态(有状态, 继续执行. 没有状态, 阻塞等待)
selector.select();
//6.获取所有状态的迭代器
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
//7.获取每一个状态
SelectionKey next = iterator.next();
//8.已经与服务端连接成功状态时
if(next.isConnectable()){
//9.正在连接中
if (socketChannel.isConnectionPending()) {
//10.设置为完成连接
socketChannel.finishConnect();
}
System.out.println("与服务端连接成功");
/* 11.向服务端发送一句话 */
socketChannel.write(ByteBuffer.wrap("客户端连接成功".getBytes()));
//12.监听读取的事件(服务端发送消息会被监听到)
socketChannel.register(selector, SelectionKey.OP_READ);
}
if(next.isReadable()){
/* 13.读取服务端消息 */
ByteBuffer allocate = ByteBuffer.allocate(1024);
socketChannel.read(allocate);
byte[] array = allocate.array();
System.out.println(new String(array, 0, allocate.position()));
/* 14.向服务端发送消息 */
socketChannel.write(ByteBuffer.wrap("在".getBytes()));
}
//15.移除当前的事件防止重复处理
iterator.remove();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}