Java NIO 系列教程(四)

阻塞式与非阻塞式(主要针对网络编程而言)

  • 阻塞式:
    比如到你某个时候到A楼一层(假如是内核缓冲区)取快递,但是你不知道快递什么时候过来,你又不能干别的事,只能死等着。

  • 非阻塞式:
    还是等快递的例子:你知道今天要来快递,等快递员给你打电话,在快递员打电话之前你可以忙自己的事情,等接到电话后再过去取,这就是非阻塞式的。

为进一步提高IO的效率,使用了多线程来解决这个问题,但是线程的数量总是会因为CPU的切换到达负荷值。

这里写图片描述

选择器是用来监控通道的I/O的状态,等Client的数据准备就绪,就通知Server来处理,这期间Server中的线程可以做其他的事情。

使用NIO完成网络通信的三个核心

1)连接Channel
2)缓冲区Buffer
3)选择器Select:用于监控SelectbleChannel的IO状态。

阻塞式网络通信实现

    @Test
    public void Server() throws IOException {
        // 获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        FileChannel fon = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE);
        // 绑定端口号
        ssChannel.bind(new InetSocketAddress(9989));
        // 接受来自客户端的请求
        SocketChannel sChannel = ssChannel.accept();
        // 建立缓冲区用于传输数据
        ByteBuffer buf = ByteBuffer.allocate(1024);
        while (sChannel.read(buf) != -1) {
            buf.flip();
            fon.write(buf);
            buf.clear();
        }

        String str = new String("传输完成");
        buf.put(str.getBytes());
        buf.flip();
        sChannel.write(buf);

        sChannel.shutdownOutput();

        ssChannel.close();
        fon.close();
        sChannel.close();
    }

    @Test
    public void Client() throws IOException {
        // 获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9989));
        // 关联文件,将文件数据发送到客户端,并且保存接收来自客户端的数据
        FileChannel fin = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ, StandardOpenOption.WRITE);
        // 利用缓冲区保存数据
        ByteBuffer buf = ByteBuffer.allocate(1024);
        // 利用通道传输数据
        while (fin.read(buf) != -1) {
            buf.flip();
            sChannel.write(buf);
            buf.clear();
        }

        int length = 0;
        while ((length = sChannel.read(buf)) != -1) {
            buf.flip();
            System.out.println(new String(buf.array(), 0, length));
            buf.clear();
        }
        sChannel.shutdownOutput();
        sChannel.close();
        fin.close();

    }

非阻塞式实现

@Test
    public void Client() throws IOException {
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9988));
        // 切换为非阻塞式的
        sChannel.configureBlocking(false);
        // 申请缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        // 写入日期
        buf.put(new Date().toString().getBytes());
        buf.flip();
        sChannel.write(buf);
        // 关闭缓冲区
        sChannel.close();
    }

    @Test
    public void Server() throws IOException {
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        // 绑定端口号
        ssChannel.bind(new InetSocketAddress(9988));
        // 切换为非阻塞式
        ssChannel.configureBlocking(false);
        // 构造选择器
        Selector selector = Selector.open();
        // 将通道注册到相应的选择器上(后面的参数表示监控的通道的状态:包含读、写、连接、接收)
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 通过选择器轮回的方式获取选择器上已经准备就绪的事件
        while (selector.select() > 0) {
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    SocketChannel scChannel = ssChannel.accept();
                    scChannel.configureBlocking(false);
                    // 监控读就绪状态
                    scChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 获取当前选择器上“读就绪状态”的通道
                    SocketChannel sChannel = (SocketChannel) selectionKey.channel();
                    // 读取数据
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    int len = 0;
                    while ((len = sChannel.read(buf)) != -1) {
                        buf.flip();
                        System.out.println(new String(buf.array()));
                        buf.clear();
                    }
                }
                // 取消选择键
                iterator.remove();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NobiGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值