栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Netty框架之Selector使用详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Netty框架之Selector使用详解

谈到Selector的具体使用,那么就要结合BIO,NIO的知识讲解,Selector使用在非阻塞模式NIO场景下,学习NIO之前先要了解BIO原理,下面我们一步步讲解

一.BIO(Blocking-IO)-阻塞IO模式

先来看一下BIO下服务端的代码

public static void main(String[] args) throws Exception {
        //创建服务端通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //监听8080端口
        ssc.bind(new InetSocketAddress(8080));  
        ssc.configureBlocking(true);//设置成阻塞模式,默认也是true
        Listlist = new ArrayList<>();
        //创建大小为16字节的Bytebuffer缓冲区用于读取channel中的数据
        ByteBuffer buffer = ByteBuffer.allocate(16);
        while(true){
            //如果有连接,就接收,返回一个SocketChannel
            SocketChannel socketChannel = ssc.accept();
            list.add(socketChannel);
            for (SocketChannel sc : list) {
                sc.read(buffer);
                buffer.flip();//切换到读模式
                //读取缓冲区中的数据
                System.out.println(Charset.defaultCharset().decode(buffer));
            } 
        }
    }

阻塞的体现在于
1.当没有连接请求的时候,线程会阻塞在accept()方法等待,直到有连接过来

SocketChannel socketChannel = ssc.accept();

2.当没有SocketChannel 通道没有消息的时候,线程会阻塞在read()方法,直到通道中有消息,线程开始读取

sc.read(buffer);

也即所有通道(ServerSocketChannel和SocketChannel)的核心方法都是阻塞模式的

二.NIO(Non Blocking-IO)-非阻塞IO模式

我们可以通过设置参数修改通道为非阻塞模式,通过configureBlocking(false),把参数设置为false,false对应非阻塞,true对应阻塞,默认为true

1.对于服务端的ServerSocketChannel

ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);

2.对于客户端的SocketChannel

SocketChannel socketChannel = ssc.accept();
            socketChannel.configureBlocking(false);

非阻塞的体现:
1.当服务端没有收到连接请求,线程不会一直阻塞在accpet()方法等待,而是继续向下执行代码流程
1.同理当通道没有消息处理时,线程也不会阻塞在read()方法等待,而是继续向下执行代码流程

三.Selector基于NIO的使用

Selector作用就是把所有的channel通道注册进去统一管理,然后调用Selector.select()监听通道是否有事件发生,如果没有就阻塞,有就通过selector.selectedKeys()获取所有事件的集合,遍历集合去处理这些通道的事件,请你尝试理解下面的服务端代码,注释很详细

public static void main(String[] args) throws Exception{
        //创建服务器通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //通道设置为非阻塞模式
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(8080));
        //轮询器
        Selector selector = Selector.open();
        //返回一个SelectionKey,可以通过它直到是哪一个channel有事件
        SelectionKey ssck = ssc.register(selector, 0, null);
        //设置为只关注连接事件OP_ACCEPT
        ssck.interestOps(SelectionKey.OP_ACCEPT);

        //创建一个字节缓冲区,大小为16字节
        ByteBuffer buf = ByteBuffer.allocate(16);
        while(true){
            //作用:判断有无事件发生,如果没有事件发生,线程阻塞,直到有事件发生为止,当然这里可以设置阻塞时长,当阻塞超过timeout,解除阻塞状态,继续向下执行代码
            selector.select();//selector.select(long timeout);
            Iterator iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()){
                //获取事件
                SelectionKey key = iterator.next();
                //处理完一个事件就移除一个,不然下次还在selectedKeys的集合中,需要手动移除
                iterator.remove();
                //如果是连接事件
                try {
                    if (key.isAcceptable()) {
                        //获取连接通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        //设置为非阻塞模式
                        sc.configureBlocking(false);
                        //加入到轮询器
                        SelectionKey sck = sc.register(selector, 0, null);
                        //设置为只监听读事件
                        sck.interestOps(SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        SocketChannel sc = (SocketChannel) key.channel();
                        sc.read(buf);
                        buf.flip();//转换为读模式
                        System.out.println(Charset.defaultCharset().decode(buf));
                        buf.clear();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    key.cancel();//事件要么被处理要么被取消,不然会一直在selector.select()的监听中
                }

            }
        }
    }

注意:
1.SelectionKey 事件要么被执行,要么被取消(调用key.cancel()方法),不然下一次调用Selector.select()旧的事件SelectionKey 会被认为没有处理,认为有事件要处理,就算没有新事件也不会阻塞

2.每次执行完一个事件SelectionKey,需要从selectedKeys()集合中删除这个key,因为集合不会自动移除这个key,需要手动调用remove()方法,不然下次湖北进行SelectionKey集合的遍历时,旧的key还会被重复执行.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/837877.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号