Netty框架之Selector使用详解

2023-05-16

谈到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
        List<SocketChannel>list = 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<SelectionKey> 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还会被重复执行.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Netty框架之Selector使用详解 的相关文章

随机推荐

  • 用STL库创建线程

    测试了3种方式 xff1a 1 xff1a 子线程不带返回值 2 xff1a 子线程带返回值 3 xff1a 子线程带引用类型参数 使用join方式 xff0c 让父线程等待子线程运行结束 TestTemp cpp 定义控制台应用程序的入口
  • 4.5树的存储

    双亲表示法 xff0c 孩子表示法 xff0c 孩子兄弟表示法 1 双亲表示法 查找双亲简单 空数据导致遍历更慢 xff0c 查指定节点的孩子只能遍历 span class token keyword typedef span ElemTy
  • Windows下MySQL数据库的安装、配置及C++使用案例

    1 安装及配置 Windows判断本地是否安装mysql以及mysql安装过程 企鹅要去银河思考人生 xff01 xff01 xff01 的博客 CSDN博客 windows查看是否安装mysql 注意按照文中提示 xff0c 配置好环境变
  • C++获取系统毫秒级时间(自1970年1月1日至今的毫秒数)

    跟系统时间相关的 ifdef WIN32 include lt time h gt include lt windows h gt else include lt sys time h gt endif unsigned long long
  • Window 10下SQL Server的安装配置以及C++使用案例

    1 SQL Server2008的安装与配置 参照下面这篇博客实现即可 里面提供了安装包下载方式 xff08 百度网盘有点慢 xff09 安装及配置步骤 SQLServer安装教程 xff08 史上最详细版本 xff09 杨林伟的博客 CS
  • 基于OpenCV实现的多角度、多目标模板匹配项目实战案例

    1 说明 本案例采用NCC的匹配 金字塔 为了加速 思想 基于OpenCV实现的多角度 多目标模板匹配 不支持尺度不变 若研究旋转 尺度不变性的匹配 请参考本人的OpenCV专栏内的 nbsp OpenCV实现多角度多尺度模板匹配 基于形状
  • 程序日志模块的两种模式

    程序员都知道程序的运行日志在不少时候都非常有用 xff0c 利于排查 理清逻辑 一般而言 xff0c 日志都按天生成 xff0c 并且具备自动清理多少天以前的旧日志 xff0c 避免无限增长占用磁盘 下图展示了2种日志模式 模式一 1 xf
  • C++开发面试常考

    C 43 43 后台开发面试常考 pudn com
  • 如何在微软官网上下载旧版本的visual studio

    想在微软官网下载旧版本的VS 太长不想看的可以直接戳网址进入最终的界面 xff1a Visual Studio 较旧的下载 2019 2017 2015 和以前的版本想从官网首页一步一步进入到最终下载界面的可以看下面详细步骤 xff1a 1
  • 基于OpenCV实现的RANSAC随机抽样一致性直线拟合

    概要 本文介绍基于ransac随机抽样一致性随机抽样一致性的直线拟合方法 涵盖一下的内容 ransac的算法思想 ransac的算法步骤 如何调整ransac算法迭代的次数 基于opencv编码实现 ransac算法流程 RANSACRAN
  • 基于OpenCV(C++)实现的RANSAC随机抽样一致性的曲线拟合(二次)

    nbsp 0 前言 nbsp nbsp 前不久整理了RANSAC直线拟合的文章 基于OpenCV实现的RANSAC随机抽样一致性直线拟合 thequitesunshine007的博客 CSDN博客 这篇文章与其类似 只是从拟合直线变为拟合曲
  • 最小二乘least-squares拟合曲线(三次或多次)

    1 说明 基于最小least squares去拟合出多次曲线 考虑到了所有的样本点 因此这种方法对噪声敏感 尤其是遇到较为突兀明显的噪声时 曲线的形状易受干扰 2 代码 代码细节仔细读基本都能读懂 或者查一下也不是什么大问题 include
  • 4.6树和森林的遍历

    一 树的遍历 1 先根遍历 对应二叉树先序遍历 span class token keyword void span span class token function PreOrder span span class token punc
  • 扩展欧几里得算法(简单易懂,详细分析)

    扩展欧几里得算法 扩展欧几里得算法证明 43 应用 61 61 欧几里得算法 61 61 61 61 扩展欧几里得算法 61 61 应用1 xff1a 求一元二次线性方程的整数解应用二 xff1a 求ax 61 c mod p 应用三 xf
  • 佩尔方程(超详细推导+例题讲解) 每日一遍,算法再见!

    这里写目录标题 佩尔方程第一类佩尔方程第一类佩尔方程例题讲解 第二类佩尔方程 佩尔方程 第一类佩尔方程 定义 xff1a 形如 x 2 d
  • FFT(傅里叶快速变换,详细讲解+推导) 每日一遍,算法再见!

    FFT详细推导 FFT 傅里叶快速变换 一 前置知识1 复数和单位根2 单位根的三个引理3 多项式 二 FFT 快速傅里叶变换推导 三 IFFT四 FFT求解多项式乘积模板代码1 递归版2 非递归版 这个更快 xff0c 省去了递归时间 五
  • 背包问题----分组背包(超详细讲解)

    树形dp 分组背包 43 依赖背包 分组背包1 定义2 讲解3 练习题 依赖背包1 定义2 讲解3 练习题 分组背包 1 定义 分组背包 xff0c 通俗的讲就是 xff0c 给你N组物品 xff0c 然后每一组你至多选择一个物品 也可以不
  • 树状数组(详细分析+应用),看不懂打死我!

    树状数组介绍 在学习一个算法之前一定要清楚它能干嘛 xff0c 能解决什么样的问题 xff0c 对你解题是否有帮助 xff0c 然后才去学习它 那么接下来看如下几个问题 什么是树状数组 顾名思义就是一个结构为树形结构的数组 xff0c 于二
  • 更新Win10版本后,wifi图标不见了,并且连接不到wifi和宽带,以及点击网络和Internet闪退的情况

    问题描述 你们是不是遇到过这样的问题 xff0c 更新系统前还好好的 xff0c 更新之后wifi图标不见了 于是你尝试点击最右边的图标打开wifi xff0c 如下图所示 但是你发现没有任何可连接wifi xff0c 就下面这样 xff0
  • Netty框架之Selector使用详解

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