下图是 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 ) 服务 ;
Buffer 常用子类 :
上述 ByteBuffer 使用频率最高 , 一般情况下 , 传输数据使用 ByteBuffer 进行数据的传输 ;
缓冲区 ( Buffer ) : 缓冲区 ( Buffer ) 是 在内存中开辟出一块内存 , 并提供一组 API 专门用于读写内存中的数据 ;
缓冲区 ( Buffer ) 机制 : 其内部提供了一系列机制 , 如记录当前的操作 ( 读取 / 写出 ) 位置 等实时信息 ;
缓冲区 ( Buffer ) 标志位 :
① mark : 标记 , 用途由开发者自定义 ;
② position : 标识当前数组索引 ;
③ limit : 缓冲区当前的限制大小 , 如果当前的 position 大于 limit 值 , 无法进行读写操作 , 该值可以修改 ;
④ capacity : 缓冲区 ( Buffer ) 容量 , 缓冲区创建时设置 , 无法修改该容量值 ;
mark
position
limit
capacity
解析 缓冲区 ( 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 . 创建 Buffer 缓冲区 : 此时创建了一个空的缓冲区 ,
个元素默认初始化为 0 , position
为
, limit
和 capacity
为
;
2 . 设置 limit 限制 : 只使用
个容器中的
个 , 如果 position
3 , 读写时报异常 ;
3 . 向 Buffer 中写入数据 :
① 向 Buffer 中写入
: 写入后 , position 变为
, 第 0 个元素变为 0 ( 看不出来 ) ;
② 向 Buffer 中写入
: 写入后 , position 变为
, 第 1 个元素变为 1 ;
③ 向 Buffer 中写入
: 写入后 , position 变为
, 第 2 个元素变为 2 ; 此时 position
limit , 如果在读写缓冲区 , 就要崩溃了 , 退出循环 , 执行下面的操作 ;
4 . 翻转操作 : 将当前的 position
设置成 limit , 然后将 position 设置成
, 清除 mark 值 ( 这里没有 ) ;
5 . 读取 缓冲区 ( Buffer ) 数据 :
① 读取 第
个数据 : 读取到
, position 变为
;
② 读取 第
个数据 : 读取到
, position 变为
;
③ 读取 第
个数据 : 读取到
, position 变为
; 至此 , 程序全部执行完毕 ;
Buffer 是抽象类 , 是所有的 缓冲区类的父类 , 其提供的以下方法 , 在其它
个类中都可以使用 ;
与标志位相关方法 :
缓冲区变换相关方法 :
判定相关方法 :
数组相关 :
字节缓冲区 ( ByteBuffer ) 是最常用的缓冲区 , 一般在客户端与服务器端交互使用的最多的就是字节缓冲区 ;
ByteBuffer 是 Buffer 的派生类 , 因此上面的 Buffer 中的所有方法都可以在 ByteBuffer 中使用 ;
缓冲区构建相关 API :