BIO、NIO、AIO的理解

先谈谈我对同步 异步 阻塞 非阻塞的认识:

同步:Client端发送请求,等待结果返回
eg: 小明去买东西,去超市买回来,东西买没买到,立即知道结果

异步:Client端发送请求,不等待结果返回,(后续等Server端通知等)
eg: 小明打电话让超市送过来,不知道东西有没有买到,不管结果

阻塞:Client端发送请求结束后,线程会被挂起
eg: 小明打完电话后一直等东西送过来,不做其他事情/小明去超市买东西不做其他事情

非阻塞:Client端发送请求结束后,线程不会被挂起
eg: 小明打完电话后去上网了,等到东西送过来/小明去超市买东西,同时可以拿个快递

同步是个过程,阻塞是线程的一种状态

/////////////////////////////////分割线//////////////////////////////////

BIO 同步阻塞
       同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 BIO一个连接是一个线程
客户端:
public class Client {

    public Client() throws IOException {
        init();
    }

    public void init() throws IOException {
        try (Socket socket = new Socket(InetAddress.getLocalHost(), 9090);
             PrintWriter printWriter = new PrintWriter(
                     new OutputStreamWriter(socket.getOutputStream(), "UTF8"))) {
            printWriter.println("this is a test message!!!");
        }
    }

    public static void main(String[] args) throws IOException {
        new Client();
    }
}

服务端:

public class Server {
    public Server() throws IOException {
        init();
    }

    public void init() throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(9090, 100,
                InetAddress.getLocalHost())) {
            Socket socket = serverSocket.accept();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream(), "UTF8"))) {
                String respone = reader.readLine();
                System.out.println("server receive:" + respone);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new Server();
    }

}

NIO 同步非阻塞
       同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,NIO是一个请求一个线程。
客户端:
public class Client {

    private final Integer PORT = 9090;


    public Client() throws IOException, InterruptedException {
        init();
    }

    public void init() throws IOException, InterruptedException {
        try (Selector selector = Selector.open(); SocketChannel socketChannel = SocketChannel.open()) {
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), PORT));
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (true) {
                if (selector.select() > 0) {
                    Iterator<SelectionKey> set = selector.selectedKeys().iterator();
                    while (set.hasNext()) {
                        SelectionKey key = set.next();
                        set.remove();
                        SocketChannel ch = (SocketChannel) key.channel();
                        if (key.isConnectable()) {
                            ch.register(selector, SelectionKey.OP_READ |
                                    SelectionKey.OP_WRITE, new Integer(1));
                            ch.finishConnect();
                        }
                        if (key.isReadable()) {
                            key.attach(new Integer(1));
                            ByteArrayOutputStream output = new ByteArrayOutputStream();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            int len = 0;
                            while ((len = ch.read(buffer)) != 0) {
                                buffer.flip();
                                byte[] bytes = new byte[buffer.remaining()];
                                buffer.get(bytes);
                                output.write(bytes);
                                buffer.clear();
                            }
                            System.out.println("接收到服务端消息:" + new String(output.toByteArray()));
                            output.close();
                        }
                        if (key.isWritable()) {
                            key.attach(new Integer(1));
                            ch.write(ByteBuffer.wrap((("这是一条测试消息")).getBytes()));
                            TimeUnit.SECONDS.sleep(5);
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        new Client();
    }
}

服务端:
public class Server {

    public Server() throws IOException {
        init();
    }

    public void init() throws IOException {
        // 创建多路复用器
        // 创建一个通道
        try (Selector selector = Selector.open();
             ServerSocketChannel socketChannel = ServerSocketChannel.open()) {
            socketChannel.configureBlocking(false);
            //绑定ip和端口
            socketChannel.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 9090), 1024);
            //监听客户端连接请求
            socketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //阻塞,直到有请求
            System.out.println("等待客户端链接...");
            while (true) {
                selector.select();
                System.out.println("客户端链接进入...");
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.iterator();
                SelectionKey key;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        if (key.isValid()) {
                            //处理新接入的请求消息
                            if (key.isAcceptable()) {
                                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                                //通过ServerSocketChannel的accept创建SocketChannel实例
                                //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                                SocketChannel sc = ssc.accept();
                                //设置为非阻塞的
                                sc.configureBlocking(false);
                                //注册为读
                                sc.register(selector, SelectionKey.OP_READ);
                            }
                            //读消息
                            if (key.isReadable()) {
                                SocketChannel sc = (SocketChannel) key.channel();
                                //创建ByteBuffer,并开辟一个1M的缓冲区
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                //读取请求码流,返回读取到的字节数
                                int readBytes = sc.read(buffer);
                                //读取到字节,对字节进行编解码
                                if (readBytes > 0) {
                                    //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                                    buffer.flip();
                                    //根据缓冲区可读字节数创建字节数组
                                    byte[] bytes = new byte[buffer.remaining()];
                                    //将缓冲区可读字节数组复制到新建的数组中
                                    buffer.get(bytes);
                                    String expression = new String(bytes, "UTF-8");
                                    System.out.println("服务器收到消息>>>:" + expression);
                                    //发送应答消息
                                    doWrite(sc, "这是一条返回信息");
                                }
                                //链路已经关闭,释放资源
                                else if (readBytes < 0) {
                                    key.cancel();
                                    sc.close();
                                }
                            }
                        }
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     * 异步发送应答消息
     */
    private void doWrite(SocketChannel channel, String response) throws IOException {
        //将消息编码为字节数组
        byte[] bytes = response.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
    }

    public static void main(String[] args) throws IOException {
        new Server();
    }

AIO 异步非阻塞
       在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。  AIO是一个有效请求一个线程 。
客户端:

服务端:

public class Client {


    public Client() throws IOException, InterruptedException {
        init();
    }

    public void init() throws IOException, InterruptedException {
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //连接服务
        socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 9090), 
                null, new CompletionHandler<Void, Void>() {
            final ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024);

            @Override
            public void completed(Void result, Void attachment) {
                //连接成功后, 异步调用OS向服务器写一条消息
                try {
                    ByteBuffer buffer = Charset.forName("UTF-8").
                            newEncoder().encode(CharBuffer.wrap("this is test aio msg"));
                    socketChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer buffer) {
                            if (buffer.hasRemaining()) {
                                socketChannel.write(buffer, buffer, this);
                            }
                        }
                        @Override
                        public void failed(Throwable exc, ByteBuffer attachment) {
                            exc.printStackTrace();
                        }
                    });
                } catch (CharacterCodingException e) {
                    e.printStackTrace();
                }
                readBuffer.clear();
                //异步调用OS读取服务器发送的消息
                socketChannel.read(readBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        try {
                            //异步读取完成后处理
                            if (result > 0) {
                                readBuffer.flip();
                                CharsetDecoder decoder = Charset.forName("utf-8").newDecoder();
                                CharBuffer charBuffer = decoder.decode(readBuffer);
                                String answer = charBuffer.toString();
                                System.out.println(Thread.currentThread().getName() + "---" + answer);
                                readBuffer.clear();
                                socketChannel.read(readBuffer, null, this);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    /**
                     * 读取失败处理
                     * @param exc
                     * @param attachment
                     */
                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("client read failed: " + exc);
                    }
                });
            }

            /**
             * 连接失败处理
             * @param exc
             * @param attachment
             */
            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("client connect to server failed: " + exc);
            }
        });
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);

    }
服务端:


public class Server {

    public Server() throws IOException {
        init();
    }

    public void init() throws IOException {
        try (AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(InetAddress.getLocalHost(), 9090), 100)) {
            serverChannel.accept(this, new AcceptHandler());
            TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * accept到一个请求时的回调
     */
    private class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Server> {
        @Override
        public void completed(final AsynchronousSocketChannel client, Server attachment) {
            try {
                System.out.println("远程地址:" + client.getRemoteAddress());
                //tcp各项参数
                client.setOption(StandardSocketOptions.TCP_NODELAY, true);
                client.setOption(StandardSocketOptions.SO_SNDBUF, 1024);
                client.setOption(StandardSocketOptions.SO_RCVBUF, 1024);

                if (client.isOpen()) {
                    System.out.println("client.isOpen:" + client.getRemoteAddress());
                    final ByteBuffer buffer = ByteBuffer.allocate(1024);
                    buffer.clear();
                    client.read(buffer, client, new ReadHandler(buffer));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void failed(Throwable exc, Server attachment) {
            exc.printStackTrace();
        }
    }


    /**
     * Read到请求数据的回调
     */
    private class ReadHandler implements CompletionHandler<Integer, AsynchronousSocketChannel> {

        private ByteBuffer buffer;

        public ReadHandler(ByteBuffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void completed(Integer result, AsynchronousSocketChannel attachment) {
            try {
                if (result < 0) {
                } else if (result == 0) {
                    System.out.println("空数据");
                } else {
                    // 读取请求,处理客户端发送的数据
                    buffer.flip();
                    CharsetDecoder decoder = Charset.forName("utf-8").newDecoder();
                    CharBuffer charBuffer = decoder.decode(buffer);
                    System.out.println("获取的数据:" + charBuffer.toString());
                    //响应操作,服务器响应结果
                    buffer.clear();
                    String res = "我给你回了一条数据";
                    buffer = ByteBuffer.wrap(res.getBytes());
                    attachment.write(buffer, attachment, new WriteHandler(buffer));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
            exc.printStackTrace();
        }
    }

    /**
     * Write响应完请求的回调
     */
    private class WriteHandler implements CompletionHandler<Integer, AsynchronousSocketChannel> {
        private ByteBuffer buffer;

        public WriteHandler(ByteBuffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void completed(Integer result, AsynchronousSocketChannel attachment) {
            buffer.clear();
        }

        @Override
        public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
            exc.printStackTrace();
        }
    }
 BIO、NIO、AIO适用场景分析: 
    BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 
    NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 
    AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。


               http://ifeve.com/selectors/

代码github:https://github.com/krauser1991/socket

发布了36 篇原创文章 · 获赞 11 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章