NIO学习笔记(5) -- Selector

5 篇文章 0 订阅

NIO学习笔记(5) – Selector

​ Selector 允许一个单一的线程来操作多个 Channel. 如果我们的应用程序中使用了多个 Channel, 那么使用 Selector 很方便的实现这样的目的, 但是因为在一个线程中使用了多个 Channel, 因此也会造成了每个 Channel 传输效率的降低.为了使用 Selector, 我们首先需要将 Channel 注册到 Selector 中, 随后调用 Selector 的 select()方法, 这个方法会阻塞, 直到注册在 Selector 中的 Channel 发送可读写事件. 当这个方法返回后, 当前的这个线程就可以处理 Channel 的事件了.

图解:

实例:

public class SelectorTest {
    public static void main(String[] args) throws IOException {
        // 开启端口列表
        int[] ports = new int[5];

        ports[0] = 5000;
        ports[1] = 5001;
        ports[2] = 5002;
        ports[3] = 5003;
        ports[4] = 5004;

        // 创建Selector
        Selector selector = Selector.open();

        // 启动服务器
        for (int i = 0; i < ports.length; i++) {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

            // 设置是否为阻塞
            serverSocketChannel.configureBlocking(false);
            ServerSocket serverSocket = serverSocketChannel.socket();

            // 创建一个套接字地址,其中IP地址为通配符地址,端口号为指定值。
            InetSocketAddress address = new InetSocketAddress(ports[i]);

            // 将 ServerSocket绑定到特定地
            serverSocket.bind(address);

            // 使用给定的选择器注册此频道,返回一个选择键。
            // SelectionKey.OP_ACCEPT 操作集位确认事件。(服务器开启必须先关联这个)
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("监听端口" + ports[i]);
        }

        // 死循环,保证服务器一直开启
        while (true) {
            // 选择一组其相应通道准备好进行I / O操作的键。
            int i = selector.select();
            System.out.println("i : " + i);
            // 获得选择器的键集。
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            System.out.println("selectionKeys" + selectionKeys);

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

            // 遍历获得SelectionKey
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 判断是不是带连接状态
                if (key.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel();

                    SocketChannel socketChannel = channel.accept();

                    socketChannel.configureBlocking(false);

                    // 连接成功之后将读注册到socketChannel中
                    socketChannel.register(selector, SelectionKey.OP_READ);

                    // Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。
                    // 没有将它删除, 那么下一次 select 时, 这个 key 所对应的 IO 事件还在 selectedKeys 中.
                    iterator.remove();

                    System.out.println("获取客户端连接:" + socketChannel);
                    // 判断是不是读
                } else if (key.isReadable()) {
                    SocketChannel channel = (SocketChannel) key.channel();

                    int byteRead = 0;
                    ByteBuffer byteBuffer = ByteBuffer.allocate(512);

                    while (true) {
                        byteBuffer.clear();

                        int read = channel.read(byteBuffer);

                        if (read <= 0) {
                            break;
                        }

                        byteBuffer.flip();

                        channel.write(byteBuffer);

                        byteRead += read;
                    }

                    System.out.println("读取" + byteRead + ", 来自" + channel);
                    iterator.remove();
                    // selector.close();
                }
            }
        }
    }

}

上诉代码需要注意的是:

  1. Selector的创建

     Selector selector = Selector.open();
    
  2. 向Selector注册通道

     serverSocketChannel.configureBlocking(false);
     socketChannel.register(selector, SelectionKey.OP_READ);
    
  3. Selectionkey的四种状态

    1. SelectionKey.OP_CONNECT //某个channel成功连接到另一个服务器称为“连接就绪”
    2. SelectionKey.OP_ACCEPT //一个server socket channel准备好接收新进入的连接称为“接收就绪”。
    3. SelectionKey.OP_READ //一个有数据可读的通道可以说是“读就绪”。
    4. SelectionKey.OP_WRITE // 等待写数据的通道可以说是“写就绪”。
  4. 监听结束一定要删除

    iterator.remove();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值