最通俗易懂的NIO讲解

一、NIO是什么?

NIO的全称是New I/O,与之相对应的是Java中传统的I/O,这里都指的是Java的API包。

传统的IO包提供的是同步阻塞IO,即当用户线程发出IO请求后,内核会去查看数据是否已经就绪,若未就绪,则用户线程会处于阻塞状态(让出CPU),当数据就绪后,内核会将数据复制到用户线程,并把结果返回给用户线程,同时接触用户线程的阻塞,同步体现在用户线程需要等待数据就绪后才能向后执行(后面的执行依赖于前面的结果)。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,线程数量也会受到。

而NIO包提供的IO是同步非阻塞IO,非阻塞体现在用户线程发起IO请求后,会直接得到返回结果,即便在数据未就绪的情况下,也能马上得到失败信息。而同步体现在用户线程需要主动去轮询直到发现数据就绪,再主动将数据从内核拷贝到用户线程。服务器实现模式为多个连接一个线程(IO多路复用),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理

传统IO模型

NIO

客户端个数IO线程

1:1

M11IO线程处理多个客户端连接)

IO类型(阻塞)

阻塞IO

非阻塞IO

IO类型(同步)

同步IO

同步IOIO多路复用)

API使用难度

简单

非常复杂

调式难度

简单

复杂

可靠性

非常差

吞吐量


二、NIO包API的用法。

(1)Channel:Java NIO中的所有I/O操作都基于Channel对象,一个Channel(通道)代表和某一实体的连接,这个实体可以是文件、网络套接字等。也就是说,通道是Java NIO提供的一座桥梁,用于我们的程序和操作系统底层I/O服务进行交互。

FileChannel:文件通道,从输入流输出流中获取实例,常见的使用场景就是从一个文件拷贝其内容到另一个文件。

FileInputStream fis = new FileInputStream("E://zfb.txt");
        FileChannel ifc = fis.getChannel();
        FileOutputStream os = new FileOutputStream("E://zfb2.txt");
        FileChannel ofc = os.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (ifc.read(buffer) != -1){
            System.out.println(1);
            buffer.flip();
            ofc.write(buffer);
            buffer.clear();
}

SocketChannel/ServerSocketChannel:套接字通道,通过静态方法获取实例,使用场景在最后会给出demo。

SocketChannel socketChannel = SocketChannel.open();

(2)Buffer:NIO中所使用的缓冲区不是一个简单的byte数组,而是封装过的Buffer类,通过它提供的API,我们可以灵活的操纵数据。与Java基本类型相对应,NIO提供了多种 Buffer 类型,如ByteBuffer、CharBuffer、IntBuffer等,区别就是读写缓冲区时的单位长度不一样(以对应类型的变量为单位进行读写)。Buffer中有3个很重要的变量,它们是理解Buffer工作机制的关键,分别是capacity (总容量)position (指针当前位置)、limit (读/写边界位置)。

接下来看三个方法flip、rewind、clear

public final Buffer flip() {//用于写模式到读模式的转换
        limit = position;//设置上届为当前位置
        position = 0;//当前位置设置为0
        mark = -1;//重置写标记
        return this;
}
public final Buffer rewind() {//设置当前位置为0
        position = 0;
        mark = -1;
        return this;
}
    public final Buffer clear() {//通过指针移动的方式清空缓冲区
        position = 0;//设置当前位置为0
        limit = capacity;//上限为容量
        mark = -1;
        return this;
    }

(3)Selector:Selector(选择器)是一个特殊的组件,用于采集各个通道的状态(或者说事件)。我们先将通道注册到选择器,并设置关心的事件,然后就可以通过调用select()方法,监听关注事件的发生。通道有4个事件可供我们监听:Accept、Connect、Read、Write。

selector.select()方法是阻塞的方法!!!若注册的通道没有事件到达,则不会向下执行。但是NIO的IO机制是非阻塞的!!!

三、NIO的编程模型——基于Reactor模式的事件驱动。

NIO是同步非阻塞的,但是提供了基于Selector的异步网络IO。这句话的意思是NIO的IO机制是同步非阻塞的,而基于Selector这个组件的编程模型(Reactor)是事件驱动的,是异步的。

下面来看一下一般采用NIO的编程模型:


demo服务端代码:

public class NIOServer {

    private final static int BUFFER_SIZE = 1024;

    private final static int PORT = 52621;

    private ServerSocketChannel serverSocketChannel;

    private ByteBuffer byteBuffer;

    private Selector selector;

    public NIOServer(){
        try {
            init();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void init() throws IOException {//初始化
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(PORT));
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);

    }

    private void listen() throws IOException {
        while (true){
            if (selector.select() == 0){
                continue;
            }
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                if (key.isAcceptable()){
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = ssc.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    replyClient(socketChannel);
                }
                if (key.isReadable()){
                    readDataFromClient(key);
                }
                iterator.remove();
            }
        }
    }

    private void readDataFromClient(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        byteBuffer.clear();
        int n;
        while ((n = socketChannel.read(byteBuffer)) > 0){
            byteBuffer.flip();
            while (byteBuffer.hasRemaining()){
                socketChannel.write(byteBuffer);
            }
            byteBuffer.clear();
        }
        if (n < 0){
            socketChannel.close();
        }
    }

    private void replyClient(SocketChannel channel) throws IOException {
        byteBuffer.clear();
        byteBuffer.put("Hello,I am server!".getBytes());
        byteBuffer.flip();
        channel.write(byteBuffer);
    }

    public static void main(String[] args) {
        try {
            new NIOServer().listen();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端代码:

public class NIOClient {

    private final static int BUFFER_SIZE = 1024;

    private final static int PORT = 52621;

    private SocketChannel socketChannel;

    private ByteBuffer byteBuffer;

    public void connect() throws IOException {
        socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", PORT));
        byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        receive();
    }

    private void receive() throws IOException {
        while (true){
            int n;
            byteBuffer.clear();
            while ((n = socketChannel.read(byteBuffer)) > 0){
                byteBuffer.flip();
                System.out.println("从服务器收到消息: " + new String(byteBuffer.array()));
                //socketChannel.write(byteBuffer);//发消息
                byteBuffer.clear();
            }
        }
    }

    public static void main(String[] args) {
        try {
            new NIOClient().connect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

发布了206 篇原创文章 · 获赞 257 · 访问量 59万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览