NIO是同步阻塞队列——>IO复用模型很像,请仔细看这幅图

NIO和IO的到底有什么区别?有什么关系?
1、NIO是以块的方式处理数据,但是IO是以最基础的字节流形式去写入和读出的。所以在效率上的话,肯定是NIO效率比IO的效率高出很多。
2、NIO不在是和IO一样用OutputStream和InputStream输入流得到的形式来进行处理数据的,但是又是基于这种流的形式,而是采用了通道和缓冲区的形式来进行数据处理的。
3、还有一点就是NIO的通道是可以双向的,但是IO的流只能是单向的。
4、还有就是NIO的缓冲区(其实也就是有个字节数组)换可以进行分片,可以建立只读缓冲区、直接
缓冲区和间接缓冲区,只读缓冲区 就是字面意思,直接缓冲区是加快IO速度,而以一种特殊的方式分配其内存的缓冲区。
先了解一下什么是通道,什么是缓冲区的概念
**

通道:

1、通道是对原IO包中的流的模拟。到任何目的地(或来自任何地方)的所有数据必须通过有个Channel对象(通道)。一个Buffer实质上是一个容器的对象,发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中,channl适应对象,可以通过他的读取和写入数据,拿NIO与原来的IO做个比较,通道就像是流。
2、正如前面提到的,所有数据都通过BUffer对象来处理。你永远不会将字节直接写入通道中,相反,你是将数据写入包含一个或者多个字节的缓冲区,同样,你不会直接从通道中读取字节,而是将数通道读入缓冲区,再从缓冲区获取字节。

buffer

1、Buffer是一个对象,他包含一些要写入或者刚读取的数据。再NIO中加入Buffer对象,体现了新库与原IO的一个重要区别,在面向IO中,在将数据直接写入或者将数据直接读到Stream对象中
2、在NIO库中,所有数据都是用缓冲区处理,读取数据时,它是直接读取到缓冲区中的,在写入数据时,它是写入到缓冲区中的任何时候访问NIO中的数据,你都是将推塔放到缓冲区中。
3、缓冲区的实质是一个数组,通常它是一个节点的数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组缓冲区提供了对数据的结构化访问,而且换可以跟踪系统的读写进程

缓冲区的类型:
ByteBUffer
charBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
标记、位置、限制和容量值遵守以下不变式:
0 <= 标记(mark) <= 位置(position) <= 限制(limit) <= 容量(capacity)

buffer实例的方法:
allocate(capacity);: 在堆上创建大小的对象
allocateDirect(capacity);在堆外空间上创建指定大小的对象
wrap(byte[])通过存在的数组创建对象
wrap(byte[],offerset,length)通过存在的数组创建对象

方法:
buffer.put() :往Buffer中写入数据 pos 位置移动
buffer.flip() :读写模式切换 -》lim指向pos,pos指向mark
buffer.get() :从Buffer中读取数据 ->pos 位置移动
buffer.clear(): 清空Buffer缓存 mark=-1,pos=0, lim=cap=capacity

第一步:
初始化的时候就是这样

第二步:
往里面写数据的时候position的位置变化如下图 ,position一直指向要写入元素的位置。
这时候,要将这些数字写入到通道中。这时就要用到buffer.flip(),进行读写切换

第三步:
当你调用完2中的方法时,这个时候就会变成下面的图了,这样的话其实就可以知道你刚刚写到buffer中的数据是在position—->limit之间,然后下一步调用clear();

第四步:
这时底层操作系统就可以从缓冲区中正确读取这 5 个字节数据发送出去了。在下一次写数据之前我们在调一下 clear() 方法。缓冲区的索引状态又回到初始位置。(其实这一步有点像IO中的把转运字节数组 char[] buf = new char[1024]; 不足1024字节的部分给强制刷新出去的意思)
**

selector(选择器):

**
可以同时管理同时来连接接,将关注的事件注册通过选择器来帮我们监听事件是否完成,在此期间用户可以做自己的事情

selector的使用步骤:
1、获取选择器实例
Selector selector = Selector.open();
2、将通道注册到选择器上,并确定关注事件channel.configureBloking(fals):通道设置为非阻塞
channel.register(selector SelectionKey.OP_READ);
3、选择器监听事件
selector.select();通过该方法监听事件是否完成

监听有三个方法:返回结果表示感兴趣的时间个数
int select() : 该方法会阻塞住,直至有感兴趣事件完成之后才会返回
int select (long timouts);该方法在指定的时间内返回,可能有时间返回,也可能没有事件返回
int selectNow();该方法不会阻塞。会立马返回,
4、遍历感兴趣的事件集合、判断那种事件完成
Iterator iterator = selector.selectedKeys().iterator();

5、关闭selector的资源
selector.close();

SelectionKey:
channel():将注册到选择器(selector)中的通道也会在SelectionKey维护中维护一个
selector():将当前的选择器实例也会在SelectionKey维护一个
boolen isValid(): //表示当前的SelectionKey是否是有效
cancel(); //取消对当前key的关注
boolen isConnectable();//可连接事件
boolen isWritable(); //可写事件
boolen isReadable(); //可读事件
boolen isAcceptable(); //可连接事件

SelectionKey下维护的事件:有4种
int OP_READ = 1 << 0; 读事件
int OP_WRITE = 1 << 2; 写事件
int OP_CONNECT = 1 << 3; 可连接事件
int OP_ACCEPT = 1 << 4; 可接受事件

编程步骤

服务端:
1、创建ServerChannel实例(open)
2、对实例绑定端口(bind)
3、将实例设置为非阻塞(configureBlocking)
4、实例化selector选择器(open)
5、将serverSocketChannel实例注册(register)到selector选择器,并关注可接受事件
6、选择器进行监听,有事件发生则返回
7、遍历感兴趣事件集合,判断是否有可接受事件
8、有可接受事件发生,获取对应通道,调用accept()方法获取SocketChannel实例
9、SocketChannel实例设置为非阻塞,将其注册到选择器,并关注读事件
10、循环第六步,遍历集合,判断是否有可读事件发生
11、通过Buffer从Channel读取数据
12、关闭打开的资源,包括selector选择器,关闭SocketChannel实例、ServerSocketChannel实例

客户端:
1、穿件SocketChannel实例
2、将SocketChannel实例设置为非阻塞
3、创建selector实例
4、连接服务端,立即返回结果不成功将channel注册到选择器中,并关注可连接事件
5、selector实例监听事件,有事件测发生返回
6、如该事件是可连接事件,则完成当前连接(finishConnect)
7、发送消息,接受消息
8、关闭资源

下面是代码:

客户端

public class NIOCilent {public static void main(String[] args) throws IOException {
//        创建实例SocketChannel.open();SocketChannel socketChannel = SocketChannel.open();//        将SocketChannel实例设置为非阻塞socketChannel.configureBlocking(false);
//        创建selector实例Selector selector = Selector.open();//        连接服务器if (!socketChannel.connect(new InetSocketAddress("127.0.0.1",5555))){System.out.println("连接为完成,注册到选择器当中");//        将Selector注册到选择器里面,并关注OP_CONNECT事件socketChannel.register(selector,SelectionKey.OP_CONNECT);
//            监听感兴趣的事件是否完成selector.select();Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){System.out.println(">>>>"+selector.selectedKeys().size());SelectionKey key = iterator.next();iterator.remove();if (key.isValid()&&key.isConnectable()){System.out.println("可连接事件完成");SocketChannel channel = (SocketChannel) key.channel();if (channel.isConnectionPending())channel.finishConnect();System.out.println("客户端连接成功");channel.configureBlocking(false);channel.register(selector,SelectionKey.OP_READ);}}}
//        连接完成进行读写操作ByteBuffer byteBuffer = ByteBuffer.allocate(1024);Scanner scanner = new Scanner(System.in);while (true){System.out.println("请输入:");String msg = scanner.nextLine();if ("exit".equals(msg)){break;}
//          向缓存写数据byteBuffer.put(msg.getBytes());byteBuffer.flip();
//        发送数据socketChannel.write(byteBuffer);
//        缓存清空byteBuffer.clear();
//        监听读事件,没有则阻塞selector.select();socketChannel.read(byteBuffer);
//            读写模式切换byteBuffer.flip();
//            通过缓冲区有效元素大小确定数组大小byte[] bytes1 = new byte[byteBuffer.remaining()];
//            从缓冲区读数据byteBuffer.get(bytes1);String msg1 = new String(bytes1);System.out.println("[recv]:"+msg1);byteBuffer.clear();}System.out.println("客户端关闭");selector.close();socketChannel.close();}
}

服务端

public class NIOServer {public static void main(String[] args) throws IOException {
//        穿建服务端的ServerSocketChannel.open()ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//        绑定端口serverSocketChannel.bind(new InetSocketAddress(5555));System.out.println("服务端启动");//        实例化选择器Selector.open();Selector selector = Selector.open();//        设置 ServerSocketChannel实例为非阻塞的形式,因为默认为阻塞的serverSocketChannel.configureBlocking(false);//        将该实例注册到选择器上,并设置关注accept事件serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);//        让选择器帮监听关注的事件 selector.select()会阻塞,之中有关注事件发生才返回
//        返回值表示关注的事件有多少个发生了while (selector.select()>0){System.out.println("有关注的事件发生");
//          selector.selectedKeys().表示关注事件的集合Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();if (key.isValid()&&key.isAcceptable()){System.out.println("有可接受事件发生");
//                表示可接受事件完成ServerSocketChannel sec = (ServerSocketChannel) key.channel();//                接受客户端的连接accept操作SocketChannel socketChannel = sec.accept();//               设置SocketChannel实例为非阻塞socketChannel.configureBlocking(false);//                 将SocketChannel注册到选择器中,并关注读事件socketChannel.register(selector,SelectionKey.OP_READ);}if (key.isValid()&&key.isReadable()){
//                    可读事件完成SocketChannel socketChannel = (SocketChannel) key.channel();//                    创建Buffer缓存ByteBuffer buffer =  ByteBuffer.allocate(1024);//                    将数据通过channel读到Buffer里int cont = 0;while ((cont = socketChannel.read(buffer))>0){//                    读写模式的切换buffer.flip();
//                  将数据读取Byte[] 数组byte[] bytes = new byte[buffer.remaining()];String msg = new String(bytes);System.out.println(msg);//                    清空缓存buffer.clear();buffer.put(msg.getBytes());buffer.flip();
//                    给客户端回复socketChannel.write(buffer);buffer.clear();}System.out.println("Client->send:  "+buffer.toString());if (cont<0){socketChannel.close();}iterator.remove();System.out.println("集合中剩余的事件个数"+selector.selectedKeys().size());}}}}
}

图片均为网上下载:侵删

NIO详解Channel、Buffer、Selector看这一篇就够了相关推荐

  1. 图文详解CDC技术,看这一篇就够了!

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 这篇文章是对变更 ...

  2. iOS动画详解(学习动画看这一篇就够了)

    2019独角兽企业重金招聘Python工程师标准>>> 原文出处:wu大维 动效设计一直是iOS平台的优势,良好的动效设计可以很好地提升用户体验.而动画则是动效的基础支撑.本动画将从 ...

  3. 【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?...

    简介 Floyd-Warshall算法(Floyd-Warshall algorithm),是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以 ...

  4. 设计模式详解(总纲)看了这篇文章还不会,你拍我

    最近一直在学习设计模式相关的知识,还是老规矩,和各位一起学习,一起探讨,本系列所发表所有内容仅代表个人观点. <简介> 说到设计模式,当初第一次听到时,第一反应就是很深奥,完全理解不了这个 ...

  5. 《Spring事务传播行为详解》经典例子 看完这篇,别的不用看了

    前言 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为.事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.这是Spring ...

  6. NIO核心之Channel,Buffer和Selector简介

    在NIO的API中,Channel就是实现非阻塞的组件,而事件分发(Dispatcher)使用的是Selector组件,在传统的I/O流(Stream)是有方向的,而NIO支持双向读写,这样就需要将流 ...

  7. Java基础——Java NIO详解(一)

    一.基本概念 1.I/0简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过str ...

  8. Java基础——Java NIO详解(二)

    一.简介 在我的上一篇文章Java NIO详解(一)中介绍了关于标准输入输出NIO相关知识, 本篇将重点介绍基于网络编程NIO(异步IO). 二.异步IO 异步 I/O 是一种没有阻塞地读写数据的方法 ...

  9. 代理后台中间件_Golang Gin 实战(十三)| 中间件详解看这一篇就够了

    6000字大章带你死磕Golang Gin中间件 在Gin的整个实现中,中间件可谓是Gin的精髓.一个个中间件组成一条中间件链,对HTTP Request请求进行拦截处理,实现了代码的解耦和分离,并且 ...

最新文章

  1. 从NCBI当中SRA数据库中下载高通量测序数据
  2. 组织应该采用集中式发电机吗?
  3. 华为机试支持python吗_4.10华为暑期实习生机试题目,python解答
  4. 悲观锁和乐观锁_面试必备之乐观锁与悲观锁
  5. python // 运算符
  6. 我参与的一个项目总结
  7. 将科学计数法的数值转化为字符
  8. 剑指offer——22.链表中倒数第k个节点
  9. bzoj 1046: [HAOI2007]上升序列
  10. border 0px和border none的区别
  11. GD32F407RTC备份寄存器BKP的使用
  12. 对不起,免费午餐现在只提供稀饭了-- MSN停止支持对第三方软件的登录请求
  13. java 银行卡归属地查询_银行卡归属地查询示例代码
  14. 魔兽世界怀旧服最新服务器开发时间,魔兽世界怀旧服明日开放,开服第一天“大部队”能升到多少级?...
  15. 九宫格,二十五宫格,甚至八十一宫格 技巧
  16. 关于在SW中怎么放样凸台基体
  17. 用canvas制作的躲避球小游戏html5源码
  18. 普惠微光汇聚暖阳,招联携手奋斗者筑梦前行
  19. Ubuntu安装GVM-11并使用gvm-tools命令行方式通讯
  20. RT_thread STM32通用Bootloader 做OTA升级

热门文章

  1. mui之 scroll采坑
  2. 一文览尽2017年下半年机器人行业融资大事件
  3. 城/市/县 字典全国
  4. 使用vite创建单页应用
  5. 1.8寸TFT LCD128X160 ST7735S SPI串口屏驱动示例
  6. MTK cts测试注意事项
  7. 大数据折射宣城旅游“智慧变革”
  8. 简历解析步骤(第二步)技术与实现(7)识文字,做分类: 身份证号 、 民族 、 国籍
  9. 艾默生流量计类型流量计的应用特点
  10. centos8安装zabbix5.0(mysql,Nginx)