1、bio nio aio区别?
BIO:block io 同步式阻塞
粗略的说就是一个连接一个线程,传统的io,
特点:简单方便使用 并发处理
能力低一直阻塞 直到操作完成
适用于:连接数目小且固定的架构但这种对服务器资源相对要求高,毕竟客户端来一个连接新建一个线程,不完成一直阻塞着;jdk1.4以前这是唯一的选择,程序简单直观。
客户端
package com.com.cn.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* Created with IntelliJ IDEA.
*
* @Author: lht
* @Date: 2022/05/07/10:05
* @Description: client 客户端 tcp三次握手四次挥手
* 客户端发送一个syn包 服务端接收到后发送一个syn+ack包 客户端在发送一个ack包确认是我给你的
* 四次挥手 客户端发送一个FIN包说我要结束了服务端收到后发送一个ack 在发一个刚才收到的fin客户端收到后发送刚接收到的ack
* 给我一个ACK我给你一个FIN,然后再次彼此交换确认,OK就可以结束通信了
*/
public class SocketTest {
final static String ADDRESS = "127.0.0.1";
final static int PORT = 7788;
public static void main(String[] args) throws Exception {
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
socket = new Socket(ADDRESS, PORT);
//从服务端获取数据
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
//向服务器端发送数据
out.println("接收到客户端的请求数据...");
out.println("接收到客户端的请求数据1111...");
String response = in.readLine();
System.out.println("Client: " + response);
}
}
服务端:
public class IoServerTest {
final static int PROT = 7788;
public static void main(String[] args) throws Exception {
ServerSocket server = null;
server = new ServerSocket(PROT);
System.out.println(" server start .. ");
//进行阻塞
Socket socket = server.accept();
//新建一个线程执行客户端的任务
new Thread(new ServerHandler(socket)).start();
}
}
创建线程池解决
public static void main(String[] args) throws Exception {
ServerSocket server = null;
server = new ServerSocket(PROT);
Socket socket = null;
System.out.println(" server start .. ");
//进行阻塞
//BIO 创建线程池解决同步阻塞 但只是解决了服务器端不会因为并发太多而死掉,但解决不了并发大而响应越来越慢的,
HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);
while(true){
socket = server.accept();
executorPool.execute(new ServerHandler(socket));
}
public class HandlerExecutorPool {
private ExecutorService executor;
public HandlerExecutorPool(int maxPoolSize, int queueSize){
this.executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
maxPoolSize,
120L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(queueSize));
}
public void execute(Runnable task){
this.executor.execute(task);
}
}
处理
public class ServerHandler implements Runnable {
private Socket socket;
public ServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String body = null;
while (true) {
body = in.readLine();
if (body == null) break;
System.out.println("Server :" + body);
out.println("服务器端回送响的应数据.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
nio new IO 同步非阻塞
nio 相当于bio的升级,服务端和客户端通过channel通道,通过调用注册的回调函数通知线程做io操作,线程开始阻塞,实现多路复用
简单的说就是:将所有的数据都到缓冲区中处理,本质是一个数组,提供了位置,容量上限等操作。
channel:通道写流最大不同是通道是双向的,而流是一个方向上移动(InputStream/OutputStream),通道可用于读/写或读写同时进行
Selector 多路复用选择器
多路复用选择器提供选择已经就绪的任务的能力,也就是selector会不断轮询注册在其上的通道(Channel),
如果某个通道发生了读写操作,这个通道处于就绪状态,会被selector轮询出来,然后通过selectionKey可以取得就绪的Channel集合,从而进行后续的IO操作
nio:适用连接多但连接较短的轻操作,例如聊天服务器,并发局限于应用中,编程复杂,从1.4开始支持。
客户端
public class NIOClientTest {
public static void main(String[] args) throws IOException {
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7788);//创建连接的地址
SocketChannel sc = null;//声明连接通道
ByteBuffer buf = ByteBuffer.allocate(1024);//建立缓冲区
sc = SocketChannel.open();//打开通道
sc.connect(address);//进行连接
while (true) {
//定义一个字节数组,然后使用系统录入功能:
byte[] bytes = new byte[1024];
System.in.read(bytes);
buf.put(bytes);//把数据放到缓冲区中
buf.flip();//对缓冲区进行复位
sc.write(buf);//写出数据
buf.clear();//清空缓冲区数据
}
}
}
服务端
使用多路复用器管理所有通道
1、打开多路复用器 打开服务器通道
2、设置为非阻塞模式
3、把服务器通道注册到多路复用器上,并监听阻塞事件
public class Server implements Runnable{
//1 多路复用器(管理所有的通道)
private Selector seletor;
//2 建立缓冲区
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
//3
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
public Server(int port){
try {
//1 打开路复用器
this.seletor = Selector.open();
//2 打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//3 设置服务器通道为非阻塞模式
ssc.configureBlocking(false);
//4 绑定地址
ssc.bind(new InetSocketAddress(port));
//5 把服务器通道注册到多路复用器上,并且监听阻塞事件
ssc.register(this.seletor, SelectionKey.OP_ACCEPT);
System.out.println("Server start, port :" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
//1 必须要让多路复用器开始监听
this.seletor.select();
//2 返回多路复用器已经选择的结果集
Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
//3 进行遍历
while(keys.hasNext()){
//4 获取一个选择的元素
SelectionKey key = keys.next();
//5 直接从容器中移除就可以了
keys.remove();
//6 如果是有效的
if(key.isValid()){
//7 如果为阻塞状态
if(key.isAcceptable()){
this.accept(key);
}
//8 如果为可读状态
if(key.isReadable()){
this.read(key);
}
//9 写数据
if(key.isWritable()){
//this.write(key); //ssc
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void write(SelectionKey key){
//ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//ssc.register(this.seletor, SelectionKey.OP_WRITE);
}
private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("Server : " + body);
// 9..可以写回给客户端数据
} catch (IOException e) {
e.printStackTrace();
}
}
private void accept(SelectionKey key) {
try {
//1 获取服务通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//2 执行阻塞方法
SocketChannel sc = ssc.accept();
//3 设置阻塞模式
sc.configureBlocking(false);
//4 注册到多路复用器上,并设置读取标识
sc.register(this.seletor, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(new Server(7788)).start();;
}
}
AIO
这里是引用
Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
同步异步 阻塞非阻塞
同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)