Spring-BootSpringBoot极简教程 · Spring Boot

NIO(第二篇)

2018-06-09  本文已影响20人  z七夜

写在前面

关于NIO的介绍和NIO的基础知识,请看第一篇https://www.jianshu.com/p/ff7f90f320e4

阻塞与非阻塞

阻塞与非阻塞:阻塞与非阻塞是进程在访问数据的时候,数据内是否准备就绪的一种处理方式,当数据没有准备好的时候,
阻塞:往往需要等待缓冲区中的数据准备好之后才能处理其他的事情,
否则一直等待在哪里
非阻塞:当我们的进程访问我们的数据缓冲区的时候,数据没有准备好
的时候,直接返回,不需要等待,数据有的时候也直接返回,

1.阻塞式NIO

和传统IO差不多,进行文本复制,客户端发送给服务端数据,服务端接收,开辟通道,使用缓冲区进行数据存取

 @Test
    public void client() throws IOException {

        //获取通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost",8888));

        //分配缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //3.读取本地文件,发送给服务端

        FileChannel fileChannel = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);

        while (fileChannel.read(byteBuffer)!=-1){
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
        }

        fileChannel.close();
        socketChannel.close();

    }


    @Test
    public void server() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));

        //客户端连接
        SocketChannel socketChannel = serverSocketChannel.accept();

        //分配缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        FileChannel outChannel = FileChannel.open(Paths.get("server.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);

        while (socketChannel.read(byteBuffer)!=-1){
                byteBuffer.flip();
                outChannel.write(byteBuffer);
            System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
                byteBuffer.clear();
        }

        //关闭通道
        outChannel.close();
        socketChannel.close();
        serverSocketChannel.close();

    }

重点:当服务端接收到客户端的文件,想给客户端返回信息,说已经收到了,该怎么做呢,

改进:
在客户端添加如下代码,当客户端发送完数据之后,读取服务端返回的数据


image.png

在服务端添加如下代码,当服务端接收完数据之后,发送数据给客户端

image.png

理论上没问题,但是,程序停不下来了,什么意思,客户端发送给服务端数据,服务端接收,客户端没停,服务端就一直在读取客户端发来的数据,但是客户端的数据已经发完了,在等待服务端返回数据,然后服务端就卡在了read方法,线程卡死,

2.非阻塞式NIO

 @Test
    public void client() throws IOException {

        //1.获取通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8888));
        //2.设置通道为非阻塞模式
        socketChannel.configureBlocking(false);

        //3.分配缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //4.填充数据
        byteBuffer = byteBuffer.put((LocalDateTime.now().toString().trim()+"\n"+"hello").getBytes());
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        byteBuffer.clear();

        //关闭通道
        socketChannel.close();
    }

    @Test
    public void server() throws IOException {

        //1.打开通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //2.切换成非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //3.绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8888));

        //4.得到选择器
        Selector selector = Selector.open();

        //5.将通道注册到选择器上
        /**
         *功能描述
         * @date 2018/6/8
         * @param 选择器
         * @param 监控通道的事件 OP_WRITE写 OP_ACCEPT接收 OP_CONNECT链接 OP_READ读
         * @return void
         */
        //指定监听接收事件
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

        //6.轮询式的获取选择器上已经准备就绪的事件
        while (selector.select()>0){
            //7.得到当前选择器中的所有的选择键(已就绪的事件)
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()){
                //8.获取已经准备就绪的事件
                SelectionKey next = iterator.next();
                //9.判断具体是什么事件准备就绪
                if (next.isAcceptable()){//链接事件就绪

                    //10.得到客户端通道
                    SocketChannel socketChannel = serverSocketChannel.accept();

                    //11.切换通道为非阻塞模式
                    socketChannel.configureBlocking(false);

                    //12.将通道注册到选择器中,监听客户端的读就绪事件
                    socketChannel.register(selector,SelectionKey.OP_READ);

                }if (next.isReadable()){//读就绪
                    //13.得到读就绪的通道
                    SocketChannel channel = (SocketChannel)next.channel();

                    //14.分配缓冲区
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                    //15.读取数据
                    while (channel.read(byteBuffer)!=-1){
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
                        byteBuffer.clear();
                    }


                }

                iterator.remove();
            }
        }



    }

服务端:

只有当某个事件准备就绪之后,才会处理,不会造成线程卡主的问题

个人见解,有错误请指正
QQ群:552113611

上一篇 下一篇

猜你喜欢

热点阅读