BIO NIO AIO 概述

1. 概述

在通信框架中经常使用到的三种通信模式,即BIO NIO AIO .
BIO NIO AIO

同步阻塞IO:用户进程发起一个IO操作后,必须等待IO操作真正完成后,才能继续运行

同步非阻塞IO:用户进程发起一个IO操作后,可以做其他事情,但用户进程需要经常询问IO操作是否完成,这样造成不必要的CPU资源浪费

异步非阻塞IO: 用户进程发起一个IO操作然后,立即返回,等IO操作真正完成以后,应用程序会得到IO操作完成的通知,类比Future模式

2. BIO

2.1 定义

BIO,全程Block-IO,是一种阻塞同步的通信模式,也就是我们常说的SocketIO一般指的是就是BIO

2.2 优点

模式简单,使用方便

2.3 缺点

并发处理能力低,通信耗时

2.4 原理

2.5 小结

BIO模型中通过Socket和ServerSocket完成套接字通道的实现,阻塞,同步,建立连接耗时
服务器提供IP地址和监听的端口,客户端使用服务器提供的IP地址和端口通过TCP三次握手与服务器,连接成功之后,通过Socket套接字通信。

2.6 示例demo

BIO服务器代码:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
import com.springboot.demo.io.Constants;
 
/**
 * BIO服务端
 * <p>
 * BIO模型中通过Socket和ServerSocket完成套接字通道的实现。阻塞,同步,建立连接耗时
 * <p>
 * IO 也称为 BIO,Block IO 阻塞同步的通讯方式
 * 比较传统的技术,实际开发中基本上用Netty或者是AIO。熟悉BIO,NIO,体会其中变化的过程。作为一个web开发人员,stock通讯面试经常问题。
 * BIO最大的问题是:阻塞,同步。
 * BIO通讯方式很依赖于网络,若网速不好,阻塞时间会很长。每次请求都由程序执行并返回,这是同步的缺陷。
 * BIO工作流程:
 * 第一步:server端服务器启动
 * 第二步:server端服务器阻塞监听client请求
 * 第三步:server端服务器接收请求,创建线程实现任务
 *
 * @date 2019-12-03 14:18
 */
public class BIOServer {
 
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 启动服务监听
            serverSocket = new ServerSocket(Constants.BIO_PORT);
            System.out.println("BIO server starting.....");
 
           /* while (true){
                // 服务器监听:阻塞,等待Client请求
                socket = serverSocket.accept();
                System.out.println("server 服务器确认请求 : " + socket);
                // 服务器连接确认:确认Client请求后,创建线程执行任务  。很明显的问题,若每接收一次请求就要创建一个线程,显然是不合理的。
                new Thread(new BIOServerHandler(socket)).start();
            }*/
 
            threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1000,
                    TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));
            while (true) {
                // 服务器监听:阻塞,等待Client请求
                socket = serverSocket.accept();
                threadPoolExecutor.execute(new BIOServerHandler(socket));
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (socket != null) {
                    socket.close();
                }
 
                if (serverSocket != null) {
                    serverSocket.close();
                    System.out.println("BIO Server closed !!!!");
                }
 
                threadPoolExecutor.shutdown();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
 
/**
 * BIO服务处理器
 *
 * @date 2019-12-03 14:25
 **/
public class BIOServerHandler implements Runnable {
    private Socket socket;
 
    public BIOServerHandler(Socket socket) {
        this.socket = socket;
    }
 
    @Override
    public void run() {
        BufferedReader bufferedReader = null;
        PrintWriter printWriter = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            printWriter = new PrintWriter(this.socket.getOutputStream(), true);
            String body = null;
            while (true) {
                // 若客户端用的是 writer.print() 传值,那readerLine() 是不能获取值的
                body = bufferedReader.readLine();
                if (body == null) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + ", BIO server receive data:" + body);
                // 返回客户端数据
                printWriter.println(body + " data is received" );
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (printWriter != null) {
                printWriter.close();
            }
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (this.socket != null) {
                    this.socket.close();
                    this.socket = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

BIO服务器主要实现了以下逻辑:
1) 服务启动,阻塞服务,端口监听客户端请求,为客户端请求分配线程处理
2) 从Socket获取请求数据,然后将返回数据写入,返回Socket套接字给客户端
BIO客户端代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
 
import com.springboot.demo.io.Constants;
 
/**
 * BIO客户端
 *
 * @date 2019-12-03 14:38
 **/
public class BIOClient {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            clientRequest(i);
        }
    }
 
    private static void clientRequest(int i){
        Socket socket = null;
        BufferedReader bufferedReader = null;
        PrintWriter printWriter = null;
        try {
            socket = new Socket(Constants.HOST, Constants.BIO_PORT);
            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            printWriter = new PrintWriter(socket.getOutputStream(),true);
            String data = "客户端" + i + "数据";
            // 向服务端发送数据
            printWriter.println(data);
            System.out.println(i + " 客户端请求返回数据 : " + bufferedReader.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(socket != null){
                    socket.close();
                }
                if(bufferedReader != null){
                    bufferedReader.close();
                }
                if(printWriter != null){
                    printWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
}

BIO客户端主要实现了以下逻辑:
1)启动客户端,即通过服务器IP地址和端口和服务器建立Socket连接,发起请求
2) 从Socket套接字中获取请求返回的数据

3. NIO

3.1 定义

NIO 全称 New IO, 也叫Non-Block IO 是一种非阻塞同步的通信方式

3.2 优点

并发能力好,吞吐量大

3.3 缺点

模式相对复杂,使用起来复杂

3.4 原理

服务端与客户端通过Channel通信,NIO在Channel上进行数据读写操作,而这些Channel都会被注册在Selector多路复用器上,Selector通过一个线程不停的轮询这些已经注册的Channel,筛选成已经准备好的Channel进行IO操作

3.5 小结

NIO模型中通过SocketChannel和ServerSockerChannel完成套接字通道的实现,非阻塞/阻塞,同步,避免TCP建立连接使用三次握手带来的开销
NIO通过一个线程轮询Channel达到处理很多客户端请求

3.6 名词解释

NIO通过Channekl通信,NIO的数据操作都是在缓冲区中进行的,使用Selector多路复用器轮询,下面分别介绍一下这些名词。

Channel(通道)

Buffer(缓冲区)

Selector(多路复用器)

3.7 示例demo

NIO 服务端代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
 
import com.springboot.demo.io.Constants;
 
/**
 * NIO服务
 * <p>
 * NIO模型中通过SocketChannel和ServerSocketChannel完成套接字通道的实现。非阻塞/阻塞,同步,避免TCP建立连接使用三次握手带来的开销
 * <p>
 * NIO 也称 New IO, Non-Block IO,非阻塞同步通信方式
 * 从BIO的阻塞到NIO的非阻塞,这是一大进步。功归于Buffer,Channel,Selector三个设计实现。
 * Buffer   :  缓冲区。NIO的数据操作都是在缓冲区中进行。缓冲区实际上是一个数组。而BIO是将数据直接写入或读取到Stream对象。
 * Channel  :  通道。NIO可以通过Channel进行数据的读,写和同时读写操作。
 * Selector :  多路复用器。NIO编程的基础。多路复用器提供选择已经就绪状态任务的能力。
 * 客户端和服务器通过Channel连接,而这些Channel都要注册在Selector。Selector通过一个线程不停的轮询这些Channel。找出已经准备就绪的Channel执行IO操作。
 * NIO通过一个线程轮询,实现千万个客户端的请求,这就是非阻塞NIO的特点。
 *
 * @date 2019-12-03 15:11
 **/
public class NIOServer {
 
    public NIOServer() {
    }
 
    private Selector startNIOServer() {
        try {
            // 1、打开多路复用器
            Selector selector = Selector.open();
            // 2、打开服务器通道(网络读写通道)
            ServerSocketChannel socketChannel = ServerSocketChannel.open();
            // 3、设置服务器通道为非阻塞模式,true 为阻塞,false 为非阻塞
            socketChannel.configureBlocking(false);
            // 4、绑定端口
            socketChannel.bind(new InetSocketAddress(Constants.NIO_PORT));
            // 5、把通道注册到多路复用器上,并监听阻塞事件
            /**
             * SelectionKey.OP_READ     : 表示关注读数据就绪事件
             * SelectionKey.OP_WRITE     : 表示关注写数据就绪事件
             * SelectionKey.OP_CONNECT: 表示关注socket channel的连接完成事件
             * SelectionKey.OP_ACCEPT : 表示关注server-socket channel的accept事件
             */
            socketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("NIO server starting......");
            return selector;
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        return null;
    }
 
    public static void main(String[] args) {
        Selector selector = new NIOServer().startNIOServer();
        new Thread(new NIOServerHandler(selector)).start();
    }
}
 
 
 
import java.io.IOException;
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 com.springboot.demo.io.Constants;
 
/**
 * NIO服务处理器
 *
 * @date 2019-12-03 15:11
 **/
public class NIOServerHandler implements Runnable {
    // 多路复用器,NIO编程的基础,负责管理通道Channel
    private Selector selector;
    // 缓冲区buffer
    private ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
 
    public NIOServerHandler(Selector selector) {
        this.selector = selector;
    }
 
 
    /**
     * 开启线程负责Selector轮询
     */
    @Override
    public void run() {
        while (true) {
            try {
                /**
                 * a.select() 阻塞到至少有一个通道在你注册的事件上就绪
                 * b.select(long timeOut) 阻塞到至少有一个通道在你注册的事件上就绪或者超时timeOut
                 * c.selectNow() 立即返回。如果没有就绪的通道则返回0
                 * select方法的返回值表示就绪通道的个数。
                 */
                // 1、多路复用器监听阻塞
                selector.select();
                System.out.println("阻塞了吗?");
                // 2、获取多路复用器已经选择的结果集
                Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                // 3、不停地轮询所有的Channel
                while (selectionKeys.hasNext()) {
                    // 4、获取当前选中的key
                    SelectionKey key = selectionKeys.next();
                    // 5、获取后便将其从容器中移除
                    selectionKeys.remove();
                    if (!key.isValid()) {
                        // 只获取有效地key
                        continue;
                    }
                    // 新来的请求
                    if (key.isAcceptable()) {
                        accept(key);
                    }
 
                    // 可读状态处理
                    if (key.isReadable()) {
                        System.out.println("开始读取数据");
                        read(key);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void read(SelectionKey key) {
        try {
            // 1、清空缓冲区数据
            byteBuffer.clear();
            // 2、获取在 selector 上注册的channel
            SocketChannel socketChannel = (SocketChannel) key.channel();
            // 3、读取数据
            int index = socketChannel.read(byteBuffer);
            if (index == -1) {
                // -1 标示无任何数据
                socketChannel.close();
                key.cancel();
                return;
            }
 
            // 4、有数据则在读取数据前进行复位操作
            byteBuffer.flip();
            // 5、根据缓冲区大小创建一个相应大小的bytes数组,用来获取值
            byte[] bytes = new byte[byteBuffer.remaining()];
            // 6、接收缓冲区数据
            byteBuffer.get(bytes);
            String requestMsg = new String(bytes);
            System.out.println("NIO server received data:" + requestMsg);
 
            // 返回响应数据给客户端
            write(socketChannel, requestMsg + " is response");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 返回数据给客户端
     *
     * @param socketChannel
     * @param s
     */
    private void write(SocketChannel socketChannel, String s) {
        try {
            // 创建ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
            // 将返回数据放入缓存区
            byteBuffer.put(s.getBytes());
            // 缓存区数据复位
            byteBuffer.flip();
            // 发送缓冲区数据
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    // 设置阻塞,等待Client请求。在传统IO编程中,用的是ServerSocket和Socket。在NIO中采用的ServerSocketChannel和SocketChannel
    private void accept(SelectionKey key) {
        try {
            // 1、获取通道服务
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
            // 2、执行阻塞方法
            SocketChannel socketChannel = serverSocketChannel.accept();
            // 3、设置通道服务为非阻塞,true 为阻塞,false 为非阻塞
            socketChannel.configureBlocking(false);
            // 通道注册到 selector 上去,并设置读取标识
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

NIO服务端主要实现了一下逻辑:
1)开启多路复用器Selector,打开通道ServerSockerChannel,通道绑定端口,注册通道
2) Selector轮询通道,处理请求请求事件,返回响应数据
NIO 客户端代码:

import java.util.Scanner;
 
/**
 * NIO客户端
 *
 * @date 2019-12-03 16:10
 **/
public class NIOClient {
 
    private static NIOClientHandler nioClientHandler;
 
    public static void startClient(){
        nioClientHandler = new NIOClientHandler();
        new Thread(nioClientHandler).start();
    }
 
 
    public static void main(String[] args) {
        // 开启客户端
        NIOClient.startClient();
 
        // 发送消息
        while(nioClientHandler.sendMsg(new Scanner(System.in).nextLine()));
    }
}
 
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
 
import com.springboot.demo.io.Constants;
 
/**
 * NIO客户端处理器
 *
 * @date 2019-12-03 15:11
 **/
public class NIOClientHandler implements Runnable {
    // 多路复用器,NIO编程的基础,负责管理通道Channel
    private Selector selector;
    private SocketChannel socketChannel;
    // 缓冲区buffer
    private ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
 
    public NIOClientHandler() {
        try {
            // 1、打开多路复用器
            selector = Selector.open();
            // 2、打开 SocketChannel 通道
            socketChannel = SocketChannel.open();
            // 3、设置为非阻塞模式,true 为阻塞,false 为非阻塞
            socketChannel.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    /**
     * 开启线程负责Selector轮询
     */
    @Override
    public void run() {
        doConnect();
        while (true) {
            try {
                /**
                 * a.select() 阻塞到至少有一个通道在你注册的事件上就绪
                 * b.select(long timeOut) 阻塞到至少有一个通道在你注册的事件上就绪或者超时timeOut
                 * c.selectNow() 立即返回。如果没有就绪的通道则返回0
                 * select方法的返回值表示就绪通道的个数。
                 */
                // 1、多路复用器监听阻塞
                selector.select();
                // 2、获取多路复用器已经选择的结果集
                Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                // 3、不停地轮询所有的Channel
                while (selectionKeys.hasNext()) {
                    // 4、获取当前选中的key
                    SelectionKey key = selectionKeys.next();
                    // 5、获取后便将其从容器中移除
                    selectionKeys.remove();
                    dealEvent(key);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void dealEvent(SelectionKey key) {
        // 只获取有效地key
        if (key.isValid()) {
            // 连接事件
            if (key.isConnectable()) {
                System.out.println("连接事件来了");
                connect(key);
            }
 
            // 可读状态处理
            if (key.isReadable()) {
                System.out.println("开始读取数据");
                read(key);
            }
        }
    }
 
    private void connect(SelectionKey key) {
        try {
            SocketChannel socketChannel = (SocketChannel) key.channel();
            if (!socketChannel.finishConnect()) {
                System.out.println("finish connect?");
                System.exit(1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    private void doConnect() {
        try {
            System.out.println("连接了。。。。");
            InetSocketAddress socketAddress = new InetSocketAddress(Constants.HOST,Constants.NIO_PORT);
            if(!socketChannel.connect(socketAddress)){
                System.out.println("connect is false?");
                socketChannel.register(selector, SelectionKey.OP_CONNECT);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    private void read(SelectionKey key) {
        try {
            // 1、清空缓冲区数据
            byteBuffer.clear();
            // 2、获取在 selector 上注册的channel
            SocketChannel socketChannel = (SocketChannel) key.channel();
            // 3、读取数据
            int index = socketChannel.read(byteBuffer);
            if (index == -1) {
                // -1 标示无任何数据
                socketChannel.close();
                key.cancel();
                return;
            }
 
            // 4、有数据则在读取数据前进行复位操作
            byteBuffer.flip();
            // 5、根据缓冲区大小创建一个相应大小的bytes数组,用来获取值
            byte[] bytes = new byte[byteBuffer.remaining()];
            // 6、接收缓冲区数据
            byteBuffer.get(bytes);
            String requestMsg = new String(bytes);
            System.out.println("NIO client received data:" + requestMsg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 返回数据给客户端
     *
     * @param content
     */
    private void write(String content) {
        try {
            // 创建ByteBuffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
            // 将返回数据放入缓存区
            byteBuffer.put(content.getBytes());
            // 缓存区数据复位
            byteBuffer.flip();
            // 发送缓冲区数据
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public boolean sendMsg(String content){
        if("stop".equals(content)){
            return false;
        }
 
        try {
            socketChannel.register(selector, SelectionKey.OP_READ);
            write(content);
        } catch (ClosedChannelException e) {
            e.printStackTrace();
        }
 
        return true;
    }
}

NIO 客户端主要实现了一下逻辑:
1)开启多路复用Selector,打开通道SocketChannl,注册通道,连接服务器
2)发送请求,发送数据
3)Selector轮询通道,处理请求响应数据事件

4. AIO

4.1 定义

AIO也叫NIO2.0,是一种非阻塞异步的通信模式,在NIO的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现

4.2 优点

相比NIO,并发能力更好,吞吐量更大

4.3 缺点

模式相对复杂,使用起来复杂

4.4 原理

AIO并没有采用NIO的多路复用器Selector,而是使用异步通道,其read,write方法的返回类型都是Future对象。而Future模型是异步的,弃核心思想是:在主函数等待数据返回即可。
AIO 并没有采用NIO的多路复用器Selector,而是使用异步通道,其read write方法返回类型都是Future对象。而Future模型是异步的,其核心思想是:在主函数等待数据返回即可

4.5 小结

AIO 模型中通过AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道的实现,非阻塞异步

4.6 示例demo

AIO 服务端代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
import com.springboot.demo.io.Constants;
 
/**
 * AIO服务
 * <p>
 * AIO 也叫NIO2.0 是一种非阻塞异步的通信模式。在NIO的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。
 * AIO 并没有采用NIO的多路复用器,而是使用异步通道的概念。其read,write方法的返回类型都是Future对象,而Future模型是异步的,
 * 其核心思想是:去主函数等待时间
 * <p>
 * 小结:AIO模型中通过AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道的实现。非阻塞,异步。
 *
 * @date 2019-12-03 17:03
 **/
public class AIOServer {
    // 线程池
    private ExecutorService executorService;
    // 通道组
    private AsynchronousChannelGroup channelGroup;
    // 服务器通道
    public AsynchronousServerSocketChannel serverSocketChannel;
 
    public AIOServer() {
    }
 
    public void startAIOServer() {
        try {
            // 1、创建一个缓存线程池
            executorService = Executors.newCachedThreadPool();
            // 2、创建通道组
            channelGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            // 3、创建服务器通道
            serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
            // 4、绑定网络
            serverSocketChannel.bind(new InetSocketAddress(Constants.AIO_PORT));
            System.out.println("AIO server starting......");
            // 等待客户端请求
            serverSocketChannel.accept(this, new AIOServerHandler());
            // 一直阻塞 不让服务器停止,真实环境是在tomcat下运行,所以不需要这行代码
            TimeUnit.MILLISECONDS.sleep(Integer.MAX_VALUE);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        new AIOServer().startAIOServer();
    }
}
 
 
 
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
 
import com.springboot.demo.io.Constants;
 
/**
 * AIO服务处理器
 *
 * @date 2019-12-03 17:17
 **/
public class AIOServerHandler implements CompletionHandler<AsynchronousSocketChannel,AIOServer> {
    @Override
    public void completed(AsynchronousSocketChannel result, AIOServer attachment) {
        // 保证多个客户端都可以阻塞
        attachment.serverSocketChannel.accept(attachment,this);
        read(result);
    }
 
    // 读取数据
    private void read(final AsynchronousSocketChannel socketChannel) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
        socketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                // 读取之后重置标记位
                attachment.flip();
                // 获取读取的数据
                String resultData = new String(attachment.array()).trim();
                System.out.println("AIO server received data:" + resultData);
                //
                String responseContent = resultData + " response";
                write(socketChannel, responseContent);
            }
 
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                exc.printStackTrace();
            }
        });
    }
 
    private void write(AsynchronousSocketChannel socketChannel, String responseContent) {
        try {
            ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
            // 数据放入缓冲区中
            byteBuffer.put(responseContent.getBytes());
            byteBuffer.flip();
            // 缓冲区中数据写入到通道中
            socketChannel.write(byteBuffer).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public void failed(Throwable exc, AIOServer attachment) {
        exc.printStackTrace();
    }
}

AIO服务端主要实现了以下逻辑:
1)创建服务器通道AsynchronousServerSocketChannel,绑定端口,等待客户端请求
2)读取请求数据,处理数据,写入返回数据
AIO客户端代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
 
import com.springboot.demo.io.Constants;
 
/**
 * AIO客户端
 *
 * @date 2019-12-03 17:32
 **/
public class AIOClient implements Runnable {
    private AsynchronousSocketChannel socketChannel;
 
    private Integer index;
 
    public AIOClient(Integer index) {
        try {
            this.index = index;
            // 打开通道
            socketChannel = AsynchronousSocketChannel.open();
            // 创建连接
            socketChannel.connect(new InetSocketAddress(Constants.HOST,Constants.AIO_PORT));
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public void run() {
        String content = index + " -> 客户端数据";
        try {
            socketChannel.write(ByteBuffer.wrap(content.getBytes())).get();
            ByteBuffer byteBuffer = ByteBuffer.allocate(Constants.BUFFER_SIZE);
            while (true){
                int count = socketChannel.read(byteBuffer).get();
                byteBuffer.flip();
                if(count == -1){
                    continue;
                }
                byte[] responseBytes = new byte[byteBuffer.remaining()];
                // 将缓冲区的数据放入到 byte数组中
                byteBuffer.get(responseBytes);
                System.out.println(index+" 客户端 receive response :" + new String(responseBytes).trim());
                break;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            AIOClient aioClient = new AIOClient(i);
            new Thread(aioClient,"t-"+i).start();
        }
    }
}
AIO客户端主要实现了以下逻辑:
1)打开通道AsynchronousSocketChannel, 连接服务器
2) 发送请求,读取请求响应返回数据
# 5.BIO NIO 和 AIO区别
BIO: IO阻塞同步通信模式,客户端和服务器连接需要三次握手,使用简单,但吞吐量小
NIO: 非阻塞同步通信模式,客户端与服务器通过Channel连接,采用多路复用器轮询注册的Channel,提高吞吐量和可靠性
AIO: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法
## 6. 总结
BIO模型通过Socket 和 ServerSocket完成套接字通道实现。阻塞,同步,连接耗时
NIO模型通过SocketChannel 和 ServerSocketChannel完成套接字通道实现。非阻塞/阻塞,同步,避免TCP建立连接使用三次握手带来的开销。
AIO模型中通过AsynchronousSocketChannel和AsynchronousServerSocketChannel完成套接字通道实现。非阻塞,异步







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值