NIO BIO AIO个人复习笔记整理

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事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值