NIO基础系列(一)

2018-07-02  本文已影响0人  维特无忧堡

前言

在讲解IO操作之前,我们可以回顾一下同步与异步、阻塞与非阻塞的概念

看到这里是不是感觉异步和非阻塞很像啊,我之前也是一脸蒙蔽,反正我这这样理解的:异步就是你把某件事交给别人去做,然后别人之后会主动告诉你做完了,而非阻塞的就是你做到这里时发现没做完,返回一个没做完的标记,然后继续往后面执行,等你下次再来,发现它做完了,就可以用了。

NIO原理

回到正题,NIO使用的是 多路复用技术 (select模式)
把读写事件交给一个单独的线程来处理,这个线程完成IO事件 的注册功能,还有不断的去轮询我们的读写缓存区,看是否有数据准备好,准备好的话就通知相应的读写事件(线程),这样的话以前的读写线程就可以做其他的事,这个阻塞的不是所有的IO线程 阻塞的是select这个线程,这样就实现了复用

一些具体的NIO概念我就不描述了,网上有很多,我只说一下主要的流程,让大家有个印象:整个处理流程是


image.png

分布演示

放放代码让大家感受一下

首先初始化

     * 服务端初始化操作
     */
    private void init(){
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(PORT));//绑定端口
            serverSocketChannel.configureBlocking(false);//设置非阻塞
            selector = Selector.open();//通过默认的SelectorProvider对象获取一个新的实例
            selectionKey = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT); //把服务端channel注册到选择器
            System.out.println("Server start :port at "+PORT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这里一定要设置非阻塞啊,如果你设置阻塞的话就没什么意义了,那么读写操作只能等到操作完才返回,这样就违背了呀

然后事件轮询监听

 /**
     * 不断的监听客户端的连接,轮询选择器
     */
    @Override
    public void run() {
        while (true){
            try {
                int count = selector.select();  //获取就绪channel
                if (count == 0)  continue;
                Iterator<SelectionKey> iterator =  selector.selectedKeys().iterator();
                while(iterator.hasNext()){
                        SelectionKey selectionKey = iterator.next();
                        handleKey(selectionKey);
                        iterator.remove();  //把事件已经出去来了,然后把他删除掉
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

接下来处理 事件

private void handleKey(SelectionKey key) throws IOException {
        ServerSocketChannel server = null;
        if (key.isAcceptable()){
            server = (ServerSocketChannel) key.channel();
            accept(server);
        }
        if (key.isValid() && key.isReadable()){
           readMsg(key);
        }
        if(key.isValid() && key.isWritable()){
            writeMsg(key);
        }
    }

如果是读事件的话,你就可以从读管道中读取数据

   client = (SocketChannel) key.channel();
        //设置buffer缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(BLOCK_SIZE);
        //将数据读到缓冲区,然后把缓冲区的数据取出来
        int readByte = client.read(buffer);
        StringBuffer buf = new StringBuffer();
        //如果读取到了数据
        System.out.println("size = "+readByte);
如果是写事件的话,你可以把相应的数据写入写管道

channel.write(ByteBuffer.wrap(attachment.toString().getBytes()));

在下一篇我将展示一个简易的聊天demo及遇到的问题

上一篇下一篇

猜你喜欢

热点阅读