BIO、NIO、AIO

BIO、NIO 和 AIO
a) BIO:Java BIO (blocking I/O), 同步阻塞,服务器实现模式为一个连
接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行
处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以
通过线程池机制改善。
b) NIO:Java NIO (non-blocking I/O),同步非阻塞,服务器实现模式为
一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,
多路复用器轮询到连接有 I/O 请求时才启动一个线程进行处理。
i. NIO 的本质就是避免原始的 TCP 建立连接使用 3 次握手的操作,减
少连接的开销
ii. Buffer-缓冲区:Buffer 是一个对象,它包含一些要写入或者要读取
的数据。在 NIO 类库中加入 Buffer 对象,体现了新库与原 IO 的一
个重要的区别。在面向流的 IO 中,可以将数据直接写入或读取到
Stream 对象中。在 NIO 库中,所有的数据都是用缓冲区处理的(读
写 )。 缓 冲 区 实 质 上 是 一 个 数 组 , 通 常 它 是 一 个 字 节 数 组
(ByteBuffer),也可以使用其他类型的数组,这个数组为缓冲区提
供了数据的访问读写等操作属性,如位置,容量,上限等概念,参考
API 文档。Buffer 类型:我们最常用的就是 ByteBuffer,实际上每
一种 java 基本类型都对应了一种缓冲区
iii. Channel(管道。通道):通道就像自来水管道一样,网络数据通过
Channel 读取和写入,通道与流不同之处在于通道是双向的,而流
只是一个方向上移动,而通道可以用于读,写或者二者同时进行,最
关键的是可以与多路复用器结合起来,有多种的状态位,方便多路复
用器去识别。事实上通道分为两大类,一类是网络读写的
(SelectableChannel),一类是用于文件操作的(FileChannel)。
iv. Selector(选择器,多路复用器):多路复用器是 NIO 编程的基础,
非常重要,多路复用器提供选择已经就绪的任务的能力。
简单说,就是 Selector 会不断地轮询注册在其上的通道
(Channel),如果某个通道发生了读写操作,这个通道就处于就绪
状态,会被 Selector 轮询出来,然后通过 SelectionKey 可以取得
就绪的 Channel 集合,从而进行后续的 IO 操作。
Selector 线程就类似一个管理者(Master),管理了成千上万个
管道,然后轮询哪个管道的数据已经准备好,通知 CPU 执行 IO 的
读取或写入操作。
Selector 模式:当 IO 事件(管理)注册到选择器以后,selector
会分配给每个管道一个 key 值,相当于标签。selector 选择器是以
轮询的方式进行查找注册的所有 IO 事件(管道)
当我们的 IO 事件(管道)准备就绪后,select 就会识别,会通
过 key 值来找到相应的管道,进行相关的数据处理操作(从管道里
读或写数据,写道我们的数据缓冲区中)
创建 NIO 服务器的主要步骤
(1)打开 ServerSocketChannel,监听客户端连接
(2)绑定监听端口,设置连接为非阻塞模式
(3)创建 Reactor 线程,创建多路复用器并启动线程
(4)将 ServerSocketChannel 注册到 Reactor 线程中的 Selector 上,监听 ACCEPT 事件
(5)Selector 轮询准备就绪的 key
(6)Selector 监听到新的客户端接入,处理新的接入请求,完成 TCP 三次握手,简历物理链路
(7)设置客户端链路为非阻塞模式
(8)将新接入的客户端连接注册到 Reactor 线程的 Selector 上,监听读操作,读取客户端发送的网络消息
(9)异步读取客户端消息到缓冲区
(10)对 Buffer 编解码,处理半包消息,将解码成功的消息封装成 Task
(11)将应答消息编码为 Buffer,调用 SocketChannel 的 write 将消息异步发送给客户端
c) AIO:Java AIO(NIO.2) (Asynchronous I/O) , 异步非阻塞,服务器实
现模式为一个有效请求一个线程,客户端的 I/O 请求都是由 OS 先完成
了再通知服务器应用去启动线程进行处理,在 NIO 基础之上引入了异步
通道的概念。并提供异步文件和异步套接字通道的实现,从而在真正意
义上实现了异步非阻塞,之前我们学过的 NIO 只是非阻塞而非异步。而
AIO 它不需要通过多路复用器对注册的通道的进行轮训操作即可实现异
步读写,从而简化了 NIO 编程模型。也可以称为 NIO2.0,这种模式才
是真正的属于异步非阻塞的模型。
d) 联系和区别
i. 都是通过网络通信来实现的功能,比如一会我们写的代码都是在
TCP/IP 基础上进行的
ii. BIO 和 NIO 都是同步操作,而 AIO 是异步操作
iii. BIO 是阻塞情况,NIO 和 AIO 是非阻塞
阻塞和非阻塞
我们使用银行取款业务来说明,
阻塞就是银行人多的情况下,你在哪里排队等待,期间不能处理其他事
非阻塞就是银行人多的情况下,抽号坐在哪里等待,期间可以处理其他事
e) 适用场景
i. BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器
资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但
程序直观简单易理解。
ii. NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如
聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4 开始支持。
iii. AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相
册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始
支持。

NIO


public class NIODemo {
    public static void main(String[] args) {
        new NIOServer().start();
        try{
            Thread.sleep(2000);
        }catch(Exception e){
            System.out.println(e.getMessage());
        }
        for (int i = 0; i < 10; i++) {
            new NIOClient(i+1).start();
            try{
                Thread.sleep(2000);
            }catch(Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

class NIOServer extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("服务器...");
            //打开ServerSocketChannel监听客户端连接
            ServerSocketChannel channel = ServerSocketChannel.open();
            //绑定监听端口,设置连接为非阻塞模式
            channel.socket().bind(new InetSocketAddress(9999));
            channel.configureBlocking(false);
            //获取通道管理器
            Selector selector = Selector.open();
            //将通道管理器与通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件
            //只有当该事件到达时,Selector.select()会返回,否则一直阻塞
            channel.register(selector, SelectionKey.OP_ACCEPT);
            while (true){
                //当有注册的事件到达时,方法返回,否则阻塞
                selector.selectNow();
                //获取selector中的迭代器,选中项为注册的事件
                Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
                while (ite.hasNext()){
                    SelectionKey key = ite.next();
                    ite.remove();
                    if (key.isConnectable()){
                        System.out.println("11111!");
                    }else if (key.isAcceptable()){
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        //获得客户端连接通道
                        SocketChannel channel1 = server.accept();
                        channel1.configureBlocking(false);

                        //在与客户端连接成功后,为客户端通道注册SelectionKey.OP_READ事件。只有这样服务器才能接受客户端的内容
                        channel1.register(selector,SelectionKey.OP_READ);
                        System.out.println("有客户端连接了!");
                    }else if (key.isReadable()){
                        ByteBuffer buffer = ByteBuffer.allocate(30);
                        SocketChannel sc = (SocketChannel) key.channel();
                        sc.read(buffer);
                        byte[] buf = buffer.array();
                        String str = new String(buf);
                        System.out.println(str.trim());
                    }
                }
            }
        } catch (IOException e) {
            System.out.println("IOException出现了!");
        }
    }
}

class NIOClient extends Thread{
    private int num;

    public NIOClient(int num){
        this.num = num;
    }

    @Override
    public void run() {
        try{
            System.out.println("客户端..."+num+"进入了!");
            SocketChannel channel = SocketChannel.open();
            channel.configureBlocking(false);
            //获取通道管理器
            Selector selector = Selector.open();
            //客户端连接服务器,需要调用channel.finishConnection();才能实际完成连接
            InetSocketAddress address = new InetSocketAddress("localhost",9999);
            channel.connect(address);
            //为该通道注册SelectionKey.OP_CONNECT事件
            channel.register(selector,SelectionKey.OP_CONNECT);
            //选择注册过的io操作的事件(第一次为SelectionKey.OP_CONNECT)
            selector.selectNow();
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while (ite.hasNext()){
                SelectionKey key = ite.next();
                ite.remove();
                //如果正在请求连接服务器事件
                if (key.isConnectable()){
                    SocketChannel channel1 = (SocketChannel) key.channel();
                    channel1.configureBlocking(false);
                    //如果正在连接,则完成连接
                    if (channel1.isConnectionPending()){
                        channel1.finishConnect();
                    }
                    String str = num + "频道发送内容了";
                    ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
                    channel1.write(buffer);
                }
            }
        }catch(Exception e){
            System.out.println("客户端出现了异常!");
        }
    }
}

AIO

public class AIODemo {
    public static void main(String[] args) {
        AIOServer server = new AIOServer();
        AIOClient client = new AIOClient();
        AIOServer.AsyncTimeServerHandler atsh = server.new AsyncTimeServerHandler(9999);
        AIOClient.AsyncTimeClientHandler atch = client.new AsyncTimeClientHandler("localhost",9999);
        new Thread(atsh).start();
        new Thread(atch).start();

    }
}

class AIOServer {

    class AsyncTimeServerHandler implements Runnable {
        private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
        private CountDownLatch latch = null;
        public AsyncTimeServerHandler(int port) {
            try {
                // 创建一个异步的服务端通道
                asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
                // 绑定监听端口
                asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
                System.out.println("服务端启动,端口号:" + port);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            // 使用CountDownLatch来让服务端在操作完成才退出。在完成一组正在执行的操作之前,允许当前的线程一直阻塞。
            latch = new CountDownLatch(1);
            // 接收客户端的连接,使用CompletionHandler来接收accept操作成功的通知消息
            asynchronousServerSocketChannel.accept(this,new AccpetCompleteHandler());
            try {
                latch.await(); // 阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    class AccpetCompleteHandler implements CompletionHandler<AsynchronousSocketChannel,AsyncTimeServerHandler> {
        @Override
        public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) {
            // 继续接收其他的客户端连接
            attachment.asynchronousServerSocketChannel.accept(attachment,this);
            // 客户端连接成功后,使用ReadCompletionHandler读取客户端发送的信息,将信息读取到byteBuffer。
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            result.read(byteBuffer,byteBuffer,new ReadCompleteHandler(result));
        }
        @Override
        public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
            exc.printStackTrace();
            attachment.latch.countDown();
        }
    }
    class ReadCompleteHandler implements CompletionHandler<Integer,ByteBuffer> {
        private AsynchronousSocketChannel channel;
        public ReadCompleteHandler(AsynchronousSocketChannel channel) {
            if (this.channel == null) {
                this.channel = channel;
            }
        }
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            // 为后续的从缓冲区读取数据做准备
            attachment.flip();
            // 根据缓冲区的可读字节数创建数组
            byte[] bytes = new byte[attachment.remaining()];
            // 将缓冲区的可读数据读取到bytes数组
            attachment.get(bytes);
            try {
                String body = new String(bytes,"UTF-8");
                System.out.println("接收到客户端请求:" + body);

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            exc.printStackTrace();
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

class AIOClient {

    class AsyncTimeClientHandler implements CompletionHandler<Void,AsyncTimeClientHandler>,Runnable {
        private String host;
        private int port;
        private CountDownLatch latch ;
        private AsynchronousSocketChannel client;
        public AsyncTimeClientHandler(String host,int port) {
            this.host = host;
            this.port = port;
            try {
                client = AsynchronousSocketChannel.open();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {
            // 使用CountDownLatch,防止异步操作还未完成就退出了。
            latch = new CountDownLatch(1);
            // 异步连接服务端
            client.connect(new InetSocketAddress(host,port),this,this);
            try {
                latch.await(); // 阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                client.close(); // 操作完成后,关闭通道
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void completed(Void result, AsyncTimeClientHandler attachment) {
            // 要向服务器发送的数据
            byte[] data = "Hello,World!".getBytes();
            final ByteBuffer buffer = ByteBuffer.allocate(data.length);
            buffer.put(data);
            buffer.flip();
            // 异步写消息给服务端
            client.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                public void completed(Integer result, ByteBuffer attachment) {
                    // 如果有消息还没有发送完毕,则继续发送
                    if (buffer.hasRemaining()) {
                        client.write(attachment,attachment,this);
                    }
                }
                public void failed(Throwable exc, ByteBuffer attachment) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    latch.countDown();
                }
            });
        }
        @Override
        public void failed(Throwable exc, AsyncTimeClientHandler attachment) {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            latch.countDown();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值