BIO NIO AIO实例

本文详细介绍了 Java 中 BIO、NIO 和 AIO 的概念及其应用场景,并提供了具体的代码示例,帮助读者理解不同 IO 模型的特点及适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载 本文介绍java网络IO方面的知识,包含BIO、NIO和AIO的使用例子。

1.IO 术语

BIO 同步阻塞IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理。

NIO 同步非阻塞IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。

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

上面涉及到两对词语,同步和异步,阻塞和非阻塞。 同步 指用户进程触发IO操作并等待IO操作是否就绪,通常使用轮询方法查看。 异步 指用户进程触发IO操作后就做自己的事情,当IO操作完成时会得到IO操作完成的通知,通常是把回调函数给通知者。 阻塞 指当读写文件操作符时,如果当时数据没有准备好或者不可读写,用户程序就进入等待状态,直到数据准备好或者可以读写为止。 非阻塞 相对于阻塞,当数据没有准备好或者不可读写,立刻返回而不等待。

2.代码示例

为了能更快的看到这些IO具体怎么使用,下面直接show code。

2.1 BIO

1.BIO server

package com.nio.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PlainEchoServer {
    private static final ExecutorService executorPool = Executors.newFixedThreadPool(5);

    private static class Handler implements Runnable {
        private Socket clientSocket;
        public Handler(Socket clientSocket){
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
                char chars[] = new char[64];
                int len = reader.read(chars);
                StringBuffer sb = new StringBuffer();
                sb.append(new String(chars, 0, len));
                System.out.println("From client: " + sb);
                writer.write(sb.toString());
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    clientSocket.close();
                } catch (IOException ex) {
                    // ignore on close
                }
            }
        }
    }

    public void serve(int port) throws IOException {
        final ServerSocket socket = new ServerSocket(port);
        try {
            while (true) {
                long beforeTime = System.nanoTime();
                final Socket clientSocket = socket.accept();
                System.out.println("Establish connection time: " + (System.nanoTime() - beforeTime) + " ns");
                executorPool.execute(new Handler(clientSocket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        PlainEchoServer server = new PlainEchoServer();
        server.serve(8080);
    }
}

2.BIO client

package com.nio.example;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.Socket;
import java.net.UnknownHostException;

public class PlainEchoClient {
    public static void main(String args[]) throws Exception {
        for (int i = 0; i < 1; i++) {// i,20
            startClientThread(i + "name");
        }
    }

    private static void startClientThread(String name) throws UnknownHostException, IOException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    startClient();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, name);
        t.start();
    }

    private static void startClient() throws UnknownHostException, IOException {
        long beforeTime = System.nanoTime();
        String host = "127.0.0.1";
        int port = 8080;
        Socket client = new Socket(host, port);
        // 建立连接后就可以往服务端写数据了
        Writer writer = new OutputStreamWriter(client.getOutputStream());
        writer.write("Hello Server. from: " + Thread.currentThread().getName());
        writer.flush();
        // 写完以后进行读操作
        Reader reader = new InputStreamReader(client.getInputStream());
        char chars[] = new char[64];// 假设所接收字符不超过64位,just for demo
        int len = reader.read(chars);
        StringBuffer sb = new StringBuffer();
        sb.append(new String(chars, 0, len));
        System.out.println("From server: " + sb);
        writer.close();
        reader.close();
        client.close();
        System.out.println("Client use time: " + (System.nanoTime() - beforeTime) + " ns");
    }
}

2.2 NIO

1.NIO server

package com.stevex.app.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServer {

    private ByteBuffer      readBuffer;
    private Selector        selector;

    public static void main(String[] args) {
        NIOServer server = new NIOServer();
        server.init();
        server.listen();
    }

    private void init() {
        readBuffer = ByteBuffer.allocate(1024);
        ServerSocketChannel servSocketChannel;

        try {
            servSocketChannel = ServerSocketChannel.open();
            servSocketChannel.configureBlocking(false);
            // 绑定端口
            servSocketChannel.socket().bind(new InetSocketAddress(8383));

            selector = Selector.open();
            servSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void listen() {
        while (true) {
            try {
                selector.select();
                Iterator<SelectionKey> ite = selector.selectedKeys().iterator();

                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    ite.remove();// 确保不重复处理

                    handleKey(key);
                }

            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    private void handleKey(SelectionKey key) throws IOException, ClosedChannelException {
        SocketChannel channel = null;

        try {
            if (key.isAcceptable()) {
                ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                channel = serverChannel.accept();// 接受连接请求
                channel.configureBlocking(false);
                channel.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {
                channel = (SocketChannel) key.channel();
                readBuffer.clear();
                /*
                 * 当客户端channel关闭后,会不断收到read事件,但没有消息,即read方法返回-1 所以这时服务器端也需要关闭channel,避免无限无效的处理
                 */
                int count = channel.read(readBuffer);

                if (count > 0) {
                    // 一定需要调用flip函数,否则读取错误数据
                    readBuffer.flip();
                    /*
                     * 使用CharBuffer配合取出正确的数据 String question = new String(readBuffer.array());
                     * 可能会出错,因为前面readBuffer.clear();并未真正清理数据 只是重置缓冲区的position, limit, mark,
                     * 而readBuffer.array()会返回整个缓冲区的内容。 decode方法只取readBuffer的position到limit数据。 例如,上一次读取到缓冲区的是"where",
                     * clear后position为0,limit为 1024, 再次读取“bye"到缓冲区后,position为3,limit不变,
                     * flip后position为0,limit为3,前三个字符被覆盖了,但"re"还存在缓冲区中, 所以 new String(readBuffer.array()) 返回 "byere",
                     * 而decode(readBuffer)返回"bye"。
                     */
                    CharBuffer charBuffer = CharsetHelper.decode(readBuffer);
                    String question = charBuffer.toString();
                    String answer = getAnswer(question);
                    channel.write(CharsetHelper.encode(CharBuffer.wrap(answer)));
                } else {
                    // 这里关闭channel,因为客户端已经关闭channel或者异常了
                    channel.close();
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
            if (channel != null) {
                channel.close();
            }
        }
    }

    private String getAnswer(String question) {
        String answer = null;

        switch (question) {
            case "who":
                answer = "我是小娜\n";
                break;
            case "what":
                answer = "我是来帮你解闷的\n";
                break;
            case "where":
                answer = "我来自外太空\n";
                break;
            case "hi":
                answer = "hello\n";
                break;
            case "bye":
                answer = "88\n";
                break;
            default:
                answer = "请输入 who, 或者what, 或者where";
        }

        return answer;
    }
}

2.NIO client

package com.stevex.app.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class NIOClient implements Runnable {

    private BlockingQueue<String> words;
    private Random                random;

    public static void main(String[] args) {
        // 种多个线程发起Socket客户端连接请求
        for (int i = 0; i < 10; i++) {
            NIOClient c = new NIOClient();
            c.init();
            new Thread(c).start();
        }
    }

    @Override
    public void run() {
        SocketChannel channel = null;
        Selector selector = null;
        try {
            channel = SocketChannel.open();
            channel.configureBlocking(false);
            // 请求连接
            channel.connect(new InetSocketAddress("localhost", 8383));
            selector = Selector.open();
            channel.register(selector, SelectionKey.OP_CONNECT);
            boolean isOver = false;

            while (!isOver) {
                selector.select();
                Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    ite.remove();

                    if (key.isConnectable()) {
                        if (channel.isConnectionPending()) {
                            if (channel.finishConnect()) {
                                // 只有当连接成功后才能注册OP_READ事件
                                key.interestOps(SelectionKey.OP_READ);

                                channel.write(CharsetHelper.encode(CharBuffer.wrap(getWord())));
                                sleep();
                            } else {
                                key.cancel();
                            }
                        }
                    } else if (key.isReadable()) {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                        channel.read(byteBuffer);
                        byteBuffer.flip();
                        CharBuffer charBuffer = CharsetHelper.decode(byteBuffer);
                        String answer = charBuffer.toString();
                        System.out.println(Thread.currentThread().getId() + "---" + answer);

                        String word = getWord();
                        if (word != null) {
                            channel.write(CharsetHelper.encode(CharBuffer.wrap(word)));
                        } else {
                            isOver = true;
                        }
                        sleep();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (channel != null) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (selector != null) {
                try {
                    selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void init() {
        words = new ArrayBlockingQueue<String>(5);
        try {
            words.put("hi");
            words.put("who");
            words.put("what");
            words.put("where");
            words.put("bye");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        random = new Random();
    }

    private String getWord() {
        return words.poll();
    }

    private void sleep() {
        try {
            TimeUnit.SECONDS.sleep(random.nextInt(3));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.charset 编码

package com.stevex.app.nio;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public class CharsetHelper {
    private static final String UTF_8 = "UTF-8";
    private static CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder();
    private static CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();

    public static ByteBuffer encode(CharBuffer in) throws CharacterCodingException{
        return encoder.encode(in);
    }

    public static CharBuffer decode(ByteBuffer in) throws CharacterCodingException{
        return decoder.decode(in);
    }
}

2.3 AIO

1.AIO server

package com.aio.example;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AIOSocketServer {
    public static final int    PORT = 8082;
    public static final String HOST = "localhost";

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

    public AIOSocketServer() throws IOException, InterruptedException, ExecutionException {
        // open a server channel and bind to a free address, then accept a connection
        System.out.println("Open server channel");
        SocketAddress address = new InetSocketAddress(HOST, PORT);
        AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(address);
        System.out.println("Initiate accept");
        Future<AsynchronousSocketChannel> future = server.accept();

        // wait for the accept to finish
        AsynchronousSocketChannel worker = future.get();
        System.out.println("Accept completed");

        ByteBuffer readBuffer = ByteBuffer.allocate(100);
        try {
            // read a message from the client, timeout after 10 seconds
            worker.read(readBuffer).get(10, TimeUnit.SECONDS);
            System.out.println("Message received from client: " + new String(readBuffer.array()));

            // send a message to the client
            ByteBuffer message = ByteBuffer.wrap("hello client, i am Alice.".getBytes());
            worker.write(message);    
        } catch (TimeoutException e) {
            System.out.println("Client didn't respond in time");
        }

        server.close();
    }
}

2.AIO client

package com.aio.example;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * 
 * @author macun 2015年11月19日 上午9:56:00
 */
public class AIOSocketClient {

    public static final int    PORT = 8082;
    public static final String HOST = "localhost";

    public static void main(String[] args) throws IOException {
        // create a client
        SocketAddress address = new InetSocketAddress(HOST, PORT);
        ClientWrapper client = new ClientWrapper(address);
        // start client thread
        client.start();
        try {
            client.join();
        } catch (InterruptedException e) {
            System.out.println(e);
        }
        client.close();
    }

    public static class ClientWrapper extends Thread {

        AsynchronousSocketChannel client;
        Future<Void>              connectFuture;

        public ClientWrapper(SocketAddress server) throws IOException{
            // open a new socket channel and connect to the server
            System.out.println("Open client channel");
            client = AsynchronousSocketChannel.open();
            System.out.println("Connect to server");
            connectFuture = client.connect(server);
        }

        public void run() {
            System.out.println("client run.");
            // if the connect hasn't happened yet cancel it
            // if (!connectFuture.isDone()) {
            // connectFuture.cancel(true);
            // return;
            // }
            try {
                connectFuture.get();
            } catch (InterruptedException e1) {
                System.out.println("client connect error." + e1);
                return;

            } catch (ExecutionException e1) {
                System.out.println("client connect error." + e1);
                return;
            }

            try {
                // send a message to the server
                ByteBuffer message = ByteBuffer.wrap("hello server, i am Bruce.".getBytes());
                // wait for the response
                System.out.println("Sending message to the server...");
                Integer countBytes = client.write(message).get();
                System.out.println(countBytes);

                final ByteBuffer readBuffer = ByteBuffer.allocate(100);
                // Future<Integer> numberBytes = client.read(readBuffer);
                client.read(readBuffer, null, new CompletionHandler<Integer, Object>() {

                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("Message received from server: " + new String(readBuffer.array()));
                        clearUp();
                    }

                    @Override
                    public void failed(Throwable e, Object attachment) {
                        System.err.println("Exception performing write");
                        e.printStackTrace();
                        clearUp();
                    }

                    private void clearUp() {
                        try {
                            client.close();
                        } catch (IOException e) {
                            System.out.println(e);
                        }
                    }
                });

            } catch (InterruptedException e) {
                System.out.println(e);
            } catch (ExecutionException e) {
                System.out.println(e);
            }
        }

        public void close() throws IOException {
            client.close();
        }
    }
}

3.三种IO适用场景建议

1.BIO:适用于连接数目比较小且固定的架构,对服务器资源要求比较高,并发局限于应用中。jdk1.4以前的唯一选择,程序直观简单。 2.NIO:适用于连接数目较多且连接较短的应用,比如聊天服务器,并发局限于应用中,编程相对复杂,jdk1.4后开始支持,jetty就是采用的NIO。 3.AIO:适用于连接数目较多且连接较长的应用,比如相册服务器,充分调用OS参与并发操作,编程比复杂,jdk1.7开始支持。

4.IO方面推荐文章 1.深入分析java I/O 的工作机制

2.NIO2 入门

转载于:https://my.oschina.net/findurl/blog/1829647

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值