【Netty】NIO 缓冲区 ( Buffer ) 组件

举报
韩曙亮 发表于 2022/01/11 00:36:02 2022/01/11
【摘要】 文章目录 I . NIO 三大核心组件 对应关系II . 缓冲区 ( Buffer ) 类III . 缓冲区 ( Buffer ) 机制IV . 缓冲区 ( Buffer ) 机制 示例解析V ....





I . NIO 三大核心组件 对应关系



下图是 NIO 三大核心组件 , 选择器 ( Selector ) , 通道 ( Channel ) , 缓冲区 ( Buffer ) , 与 服务器端线程 , 客户端 , 结构图 ;

在这里插入图片描述

1 . 解析上图 NIO 核心组件对应关系 :


① 通道 缓冲区 对应 : 每个 通道 ( Channel ) 都对应一个 缓冲区 ( Buffer ) ;

② 选择器 通道 关联方式 : 每个 选择器 ( Selector ) 对应多个 通道 ( Channel ) , 通道 ( Channel ) 是通过注册的方式 , 注册给 选择器 ( Selector ) ;

③ 选择器 线程 对应 : 每个 选择器 ( Selector ) 对应一个线程 ;

④ 线程 通道 对应 : 每个线程 对应多个 通道 ( Channel ) ;



2 . 引申要素分析 :


① BIO 单向流机制 : BIO 中使用 Socket 进行通信时 , 每个流都是 单向 的 , 输入流只能读取数据 , 不能写出数据 ; 输出流只能写出数据 , 不能读取数据 ;

② 缓冲区 ( Buffer ) 本质 : 缓冲区 ( Buffer ) 本质是一个数组 ;

③ 缓冲区 ( Buffer ) 双向机制 : NIO 中的 缓冲区 ( Buffer ) 是 双向 的 , 既可以读取数据 , 又可以写出数据 , 但是注意读写的方向是相反的 , 读取状态 转为 写出状态时 , 需要调用 flip() 方法翻转 缓冲区 ( Buffer ) ;

④ 通道 ( Channel ) 双向机制 : 通道 ( Channel ) 负责读写 缓冲区 ( Buffer ) , 因此 通道 ( Channel ) 也必须是双向的 ;

⑤ 事件 ( Event ) : 事件 ( Event ) 决定 选择器 ( Selector ) 选择哪条 通道 ( Channel ) , 决定线程 为哪个 通道 ( Channel ) 服务 ;





II . 缓冲区 ( Buffer ) 类



Buffer 常用子类 :

  • ByteBuffer : 字节 缓冲区 ;
  • ShortBuffer : 短整型 缓冲区 ;
  • CharBuffer : 字符 缓冲区 ;
  • IntBuffer : 整型 缓冲区 ;
  • LongBuffer : 长整型 缓冲区 ;
  • DoubleBuffer : 双精度浮点型 缓冲区 ;
  • FloatBuffer : 单精度浮点型 缓冲区 ;

上述 ByteBuffer 使用频率最高 , 一般情况下 , 传输数据使用 ByteBuffer 进行数据的传输 ;





III . 缓冲区 ( Buffer ) 机制



缓冲区 ( Buffer ) : 缓冲区 ( Buffer ) 是 在内存中开辟出一块内存 , 并提供一组 API 专门用于读写内存中的数据 ;


缓冲区 ( Buffer ) 机制 : 其内部提供了一系列机制 , 如记录当前的操作 ( 读取 / 写出 ) 位置 等实时信息 ;



缓冲区 ( Buffer ) 标志位 :


① mark : 标记 , 用途由开发者自定义 ;

② position : 标识当前数组索引 ;

③ limit : 缓冲区当前的限制大小 , 如果当前的 position 大于 limit 值 , 无法进行读写操作 , 该值可以修改 ;

④ capacity : 缓冲区 ( Buffer ) 容量 , 缓冲区创建时设置 , 无法修改该容量值 ;


mark ≤ \leq position ≤ \leq limit ≤ \leq capacity





IV . 缓冲区 ( Buffer ) 机制 示例解析



解析 缓冲区 ( Buffer ) 机制 , 逐步 Debug 代码 , 及给出每个步骤的示意图 , 解析过程中的 1 . 2 . 3 . 标号与代码中的标号一致 ;


public class BufferDemo {
    public static void main(String[] args) {
        //1 . 创建一个存储 Int 类型数据的 Buffer , 可以存储 8 个 Int 数据
        IntBuffer buffer = IntBuffer.allocate(8);

        //2 . 设置 只 读写 3 个元素
        buffer.limit(3);

        //3 . 向 Buffer 中写入数据
        for(int i = 0; i < buffer.limit(); i ++){
            buffer.put(i);
        }

        //从 Buffer 中取出数据
        //4 . 先将 Buffer 翻转一下 , 然后读取 , 读出的数据与存储的数据顺序一样
        buffer.flip();

        //5 . 循环读取 buffer 中的 Int 数据, 维护了一个索引 ,
        //代表当前操作的数据索引 , 即 position
        while (buffer.hasRemaining()){
            System.out.println("position " + buffer.position() + " . " + buffer.get());
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1 . 创建 Buffer 缓冲区 : 此时创建了一个空的缓冲区 , 8 8 8 个元素默认初始化为 0 , position 0 0 0 , limitcapacity 8 8 8 ;

在这里插入图片描述
在这里插入图片描述


2 . 设置 limit 限制 : 只使用 8 8 8 个容器中的 3 3 3 个 , 如果 position ≥ \geq 3 , 读写时报异常 ;

在这里插入图片描述

在这里插入图片描述


3 . 向 Buffer 中写入数据 :


① 向 Buffer 中写入 0 0 0 : 写入后 , position 变为 1 1 1 , 第 0 个元素变为 0 ( 看不出来 ) ;

在这里插入图片描述
在这里插入图片描述


② 向 Buffer 中写入 1 1 1 : 写入后 , position 变为 2 2 2 , 第 1 个元素变为 1 ;

在这里插入图片描述

在这里插入图片描述


③ 向 Buffer 中写入 2 2 2 : 写入后 , position 变为 3 3 3 , 第 2 个元素变为 2 ; 此时 position ≥ \geq limit , 如果在读写缓冲区 , 就要崩溃了 , 退出循环 , 执行下面的操作 ;

在这里插入图片描述

在这里插入图片描述


4 . 翻转操作 : 将当前的 position 3 3 3 设置成 limit , 然后将 position 设置成 0 0 0 , 清除 mark 值 ( 这里没有 ) ;

在这里插入图片描述
在这里插入图片描述


5 . 读取 缓冲区 ( Buffer ) 数据 :


① 读取 第 0 0 0 个数据 : 读取到 0 0 0 , position 变为 1 1 1 ;

在这里插入图片描述
在这里插入图片描述

② 读取 第 1 1 1 个数据 : 读取到 1 1 1 , position 变为 2 2 2 ;

在这里插入图片描述

在这里插入图片描述


③ 读取 第 1 1 1 个数据 : 读取到 2 2 2 , position 变为 3 3 3 ; 至此 , 程序全部执行完毕 ;

在这里插入图片描述
在这里插入图片描述





V . 缓冲区 ( Buffer ) 提供的方法



Buffer 是抽象类 , 是所有的 缓冲区类的父类 , 其提供的以下方法 , 在其它 7 7 7 个类中都可以使用 ;


与标志位相关方法 :

  • int capacity() : 获取缓冲区容量 ;
  • int position() : 获取缓冲区当前读取或写出的索引位置 , 每读取 / 写出 一个元素 , 位置都会自增 , 指向下一个将要读取 / 写出的位置 ;
  • Buffer position (int newPosition) : 强行设置缓冲区的位置 , 跳转到指定的位置读取或写出数据 ;
  • int limit() : 获取缓冲区可操作性限制大小 ;
  • Buffer limit(int newLimit) : 设置缓冲区的新的可操作限制大小 ;
  • Buffer mark() : 将当前的 position 设置为 mark ;
  • Buffer reset() : 将 position 设置成 mark 的位置 ;

缓冲区变换相关方法 :

  • Buffer clear() : 清除缓冲区的四大标志位 , 缓冲区的数据保持不变 ;
  • Buffer flip() : 翻转操作 , limit 设置成当前 position 值 , position 设置为 0, mark 清除 ;
  • Buffer rewind() : 重绕操作 ;
  • Buffer remaining() : 获取当前 position 和 limit 的差值 ;

判定相关方法 :

  • boolean hasRemaining() : position 和 limit 是否相等 , 之间还有没有元素 ;
  • boolean isReadOnly() : 当前缓冲区是否是可读缓冲区 ;
  • boolean isDirect() : 该缓冲区是否是直接缓冲区 ;

数组相关 :

  • boolean hasArray() : 该缓冲区的底层实现数组是否可以访问 ;
  • Object array() : 获取缓冲区的底层数组 ;
  • int arrayOffset() : 获取 缓冲区第一个元素在底层数组中的索引 ;




VI . 字节缓冲区 ( ByteBuffer ) 提供的方法



字节缓冲区 ( ByteBuffer ) 是最常用的缓冲区 , 一般在客户端与服务器端交互使用的最多的就是字节缓冲区 ;

ByteBuffer 是 Buffer 的派生类 , 因此上面的 Buffer 中的所有方法都可以在 ByteBuffer 中使用 ;


缓冲区构建相关 API :

  • ByteBuffer allocateDirect(int capacity) : 创建直接字节缓冲区 , 并指定缓冲区大小 ;
  • ByteBuffer allocate(int capacity) : 创建字节缓冲区 , 并指定缓冲区大小 ;
  • ByteBuffer wrap(byte[] array) : 创建字节缓冲区 , 将 byte 数组放入缓冲区中 ;
  • ByteBuffer wrap(byte[] array , int offset , int length) : 创建字节缓冲区 , 将 byte 数组 的 第 offset 个元素开始的 length 个元素 放入缓冲区中 ;

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/106313829

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。