java NIO编程

122 阅读3分钟

简介

NIO是非阻塞的I/O。java NIO基于linux的多路复用I/O模型,epoll机制。

I/0复用模型:Linux提供select/poll,进程通过将一个或多个fd传递给 select 或poll系统调用,阻塞在select操作上,这样select/poll可以帮我们侦测多个fd是否处于就绪状态。select/poll是顺序扫描fd是否就绪,而且支持的f数量有限,因此它的使用受到了一些制约。Linux还提供了一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。 -------此段文字和下图摘自《《Netty权威指南》》

image.png

NIO相关类库和概念介绍

Buffer

image.png

Buffer是缓冲区,缓冲区是特定基本类型的元素的线性有限序列。除此之外,缓冲区的基本属性还包括其容量、限制和位置(capacity、limit、position)。

capacity

capacity是Buffer的容量。缓冲区的容量永远不会为负数,也永远不会改变。

limit

缓冲区的限制,是一个不应读取或写入的元素的索引。缓冲区的限制永远不会为负数,并且永远不会大于其容量。 读模式的时候,limit是限制读取的位置,写模式的时候,limint是Buffer的最大容量等于capacity

position

缓冲区的位置,是要读取或写入的下一个元素的索引。缓冲区的位置永远不会为负数,也永远不会大于其限制。 读模式的时候position是,读取的位置,写模式的时候,position是写入数据的位置指针。

关于Buffer的其他api(如clear(),flip(),rewind()等)本文不做详细介绍,具体参考java api文档。

Channel

Channel是一个通道,I/O操作的枢纽,通道表示与实体(如硬件设备、文件、网络套接字或程序组件)的开放连接,这些实体能够执行一个或多个不同的 IO 操作 (例如,读取或写入)。Channel是双全工的,读取和写入可通过Channel。

image.png

Selector

Selector是多路复用器。Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/0操作。

代码事例

与Bio Socket类和 ServerSocket类相对应,NIO也提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现.

NIOServer

public static void main(String[] args) throws IOException {
    NIOServerHandler server = new NIOServerHandler();
    Thread t = new Thread(server);
    t.start();


}

NIOServerHandler

public class NIOServerHandler implements Runnable {

    private ServerSocketChannel sers;


    private Selector selector;


    private volatile boolean running;


    public NIOServerHandler() throws ClosedChannelException {
        try {
            //打开服务套接字通道
            this.sers = ServerSocketChannel.open();
            //设置为非阻塞
            sers.configureBlocking(false);
            //获取ServerSocket
            ServerSocket socket = sers.socket();
            //绑定端口
            socket.bind(new InetSocketAddress(18080));
            this.selector = Selector.open();
            //注册连接事件
            sers.register(this.selector, SelectionKey.OP_ACCEPT);
            running = true;
        } catch (IOException e) {
            System.exit(1);
        }

    }

    @Override
    public void run() {

        while (running) {

            try {
                //
                selector.select(1000);

                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    try {

                        if (key.isValid()) {
                            if (key.isAcceptable()) {
                                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                                SocketChannel sc = ssc.accept();
                                sc.configureBlocking(false);
                                sc.register(this.selector, SelectionKey.OP_READ);
                            }

                            if (key.isReadable()) {
                                SocketChannel socketChannel = (SocketChannel) key.channel();
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                int read = socketChannel.read(buffer);

                                if (read > 0) {
                                    //读取客服端消息
                                    buffer.flip();
                                    byte[] bytes = new byte[buffer.remaining()];
                                    buffer.get(bytes);
                                    String msg = new String(bytes, "UTF-8");
                                    System.out.println("服务器接收到了客户端消息:" + msg);
                                    //给客户端响应消息
                                    String msgToClient = "hell client----";
                                    ByteBuffer bufferToClient = ByteBuffer.wrap(msgToClient.getBytes());
                                    socketChannel.write(bufferToClient);
                                } else if (read < 0) {
                                    key.cancel();
                                    socketChannel.close();
                                } else {
                                    //读取0字节,忽略
                                }
                            }
                        }
                    } catch (IOException e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

NIOClient

public static void main(String[] args) {
    NIOClientHandler client = new NIOClientHandler();
    Thread t = new Thread(client);
    t.start();

}

NIOClientHandler

public class NIOClientHandler implements Runnable {

    private SocketChannel sc;

    private Selector selector;

    private volatile boolean running;


    public NIOClientHandler() {
        try {
            selector = Selector.open();
            sc = SocketChannel.open();
            sc.configureBlocking(false);
            running = true;
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }


    }

    @Override
    public void run() {
        try {
            boolean connect = sc.connect(new InetSocketAddress(18080));
            if (connect) {
                sc.register(selector, SelectionKey.OP_READ);
                String msg = "hell server-----";
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                sc.write(buffer);
            } else {
                sc.register(selector, SelectionKey.OP_CONNECT);
            }
        } catch (IOException e) {
            System.exit(1);
        }
        while (true) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    try {
                        if (key.isValid()) {
                            SocketChannel channel = (SocketChannel) key.channel();
                            if (key.isConnectable()) {
                                if (channel.finishConnect()) {
                                    channel.register(selector, SelectionKey.OP_READ);
                                    String msg = "hell server-----";
                                    ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                                    sc.write(buffer);
                                } else {
                                    System.exit(1);
                                }


                            }
                            if (key.isReadable()) {
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                int read = channel.read(buffer);
                                if (read > 0) {
                                    buffer.flip();
                                    byte[] bytes = new byte[buffer.remaining()];
                                    buffer.get(bytes);
                                    String msg = new String(bytes, "UTF-8");
                                    System.out.println("客户端接收到了服务器的消息:" + msg);

                                } else if (read < 0) {
                                    channel.close();
                                } else {
                                    //dnt
                                }
                            }

                        }
                    } catch (IOException e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }

                }

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

            }

        }

    }
}

image.png

image.png