BIO NIO AIO实例

来源:互联网 发布:北京奥运会姚明数据 编辑:程序博客网 时间:2024/05/24 03:22

本文介绍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");    }}

以上两段代码借用建飞的 bio&nio如何选择一文。

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);    }}

以上代码来自博文:http://stevex.blog.51cto.com/4300375/1581376/

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();        }    }}

以上代码改自:http://www.ibm.com/developerworks/cn/java/j-nio2-1/


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 入门

0 0
原创粉丝点击