NIO三大核心原理示意图

  1. 每个channel(通道)都会对应一个buffer(缓冲区)
  2. Selector(选择器)对应一个线程,一个线程对应多个channel(连接)
  3. 程序切换到那个channel是由事件决定的,==Event(事件)==是一个非常重要概念
  4. Selector会根据不同的事件,在各个通道上切换
  5. Buffer就是一个内存块,底层是一个数组
  6. 数据的读写是通过Buffer,这个和BIO不同的。BIO中要么是输入流或者是输出流,不可能是双向流动的,但是NIO中的Buffer是可以读也可以写,需要用flip()方法切换
  7. channel是双向的,可以返回底层操作系统情况,比如Linux系统的底层操作通道就是双向的

NIO核心之一Buffer

  1. Buffer基本介绍:缓冲区本质上是一个可以读写数据的内存块。可以理解为一个容器对象(含数组),该对象提供一组方法,可以轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。Channel提供从文件、网络读取数据渠道,但是读取和写入数据必须经过Buffer
  2. 基本使用代码
 package com.dd.nio;import java.nio.IntBuffer;public class BasicBuffer {public static void main(String[] args) {//举例说明buffer的使用//创建一个buffer,大小为5,既可以存放5个intIntBuffer intBuffer = IntBuffer.allocate(5);//向buffer,存放数据for (int i = 0; i < intBuffer.capacity(); i++) {intBuffer.put(i*2);}//如何从buffer读取数据//将buffer转换,读写切换.不转换读不出数据intBuffer.flip();while (intBuffer.hasRemaining()){System.out.println(intBuffer.get());        }}}
  1. Buffer类定义了所有的缓存区都具有的四个属性来提供关于其包含数据元素信息

    • Capacity:容量,即可以容纳的最大数据量;在缓冲区创建时被设定并且不能该变
    • Limit:表示缓冲区的当前终点,不能对缓冲区超过极限的位置经行读写操作。且极限是可以修改的
    • Position:位置,下一个要被读或写的元素的索引,每次读写缓冲区数据时都会改变值,为下次读写准备
    • Mark:标记
  2. Buffer及其子类常用API


NIO核心之一Channel

  1. 基本介绍:通道可以同时进行读写,而流只能读或者只能写;通道可以实现异步读写数据;通道可以从缓冲读数据,也可以写数据到缓冲
  2. BIO中的stream是单向的,例如FileInputStream对象只能进行读取数据操作,而NIO中的通道(channel)是双向的,可以读操作,也可以写操作
  3. Channel在NIO中是一个接口
  4. 常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel和SocketChannel
  5. FileChannel用于文件的数据读写,DataGramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写
  6. 通道写文件实例
 package com.dd.nio;import java.io.FileOutputStream;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class NIOFileChannel {public static void main(String[] args) throws IOException {String str = "hello word";//创建一个输出流->channelFileOutputStream fileOutputStream = new FileOutputStream("d:\\file.txt");//通过fileOutputStream获取对应的FileChannel//这个fileChannel的真实类型为filechannelImplFileChannel channel = fileOutputStream.getChannel();//创建一个缓冲区byteBufferByteBuffer buffer = ByteBuffer.allocate(8 * 1024);//将 str 放入到buffer.put(str.getBytes());//对byteBuffer经行反转buffer.flip();
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200301154719337.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDgwMjU5OA==,size_16,color_FFFFFF,t_70)//将byteBuffer数据写入到channel中channel.write(buffer);fileOutputStream.close();}}
  1. 相关API解释
  2. 使用buffer完成文件的拷贝实例
package com.dd.nio;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class NIOFileChannelCopy {public static void main(String[] args) throws IOException {FileInputStream fileInputStream = new FileInputStream("1.txt");FileChannel inChannel = fileInputStream.getChannel();FileOutputStream fileOutputStream = new FileOutputStream("2.txt");FileChannel outChannel = fileOutputStream.getChannel();ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);while (true){//循环读取//重要操作,重置标志位,必须有/*public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}*/buffer.clear();int read = inChannel.read(buffer);if (read == -1 ){break;}//将buffer中的数据写入到outChannel中buffer.flip();outChannel.write(buffer);}}
}
  1. 关于Buffer和channel的注意事项和细节

    1.ByteBuffer支持类型化的put和get,放入的是什么数据类型,get就应该使用相应的数据类型取出来,否则可能BufferUnderflowException异常
    2.可以将一个普通的Buffer,转换成只读(asReadOnlyBuffer()方法)
    3.NIO还提供了MappedByteBuffer,可以让文件直接在内存(堆外内存)中进行修改,而如何同步到文件由NIO完成
    4.NIO还支持通过多个Buffer完成读写操作,即Scattering(分散)和Gathering(聚合)

package com.dd.nio;import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;/*
1.MappedByteBuffer可以让文件直接在内存(堆外内存)修改,操作系统不需要拷贝依次*/
public class MappedByteBuffer {public static void main(String[] args) throws IOException {RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw");//获取对应的通道FileChannel channel = randomAccessFile.getChannel();/** 参数1:表示使用的是读写模式* 参数2:代表可以修改的起始位* 参数3:表示映射到内存的大小,即1.txt的多少个字节映射到内存* 可以修改的范围为0-5,不包含5* 实际类型为DirectByteBuffer*/java.nio.MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);map.put(0,(byte)'h');map.put(3,(byte)'8');randomAccessFile.close();}
}
package com.dd.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;/*
scattering:将数据写入到buffer时,可以采用buffer数组,依次写入
gathering:从buffer读取数据时,亦可以采用buffer数组,依次读*/
public class ScatteringAndGathering {public static void main(String[] args) throws IOException {//使用serverSocketChannel 和 SocketChannelServerSocketChannel open = ServerSocketChannel.open();InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);//绑定端口到socket,并启动open.socket().bind(inetSocketAddress);//创建buffer数组ByteBuffer[] byteBuffers = new ByteBuffer[2];byteBuffers[0] = ByteBuffer.allocate(5);byteBuffers[1] = ByteBuffer.allocate(2);//等待客户端连接(telnet)SocketChannel socketChannel = open.accept();int messageLength = 7; //假定接受7个字节//循环的读取while (true){int byteRead = 0;while (byteRead<messageLength){long read = socketChannel.read(byteBuffers);byteRead += read;System.out.println("累计读取的字节数"+byteRead);//使用流打印,看看当前的buffer的position和limitArrays.asList(byteBuffers).stream().map(buffer -> "postion="+buffer.position()+","+buffer.limit()).forEach(System.out::println);}//将所有的buffer经行flipArrays.asList(byteBuffers).forEach(buffer -> buffer.flip());long byteWrite = 0;//将数据读出显示到客户端while(byteWrite < messageLength){long write = socketChannel.write(byteBuffers);byteWrite += write;}//将所有的buffer 进行clearArrays.asList(byteBuffers).forEach(buffer -> buffer.clear());System.out.println("byteRead="+byteRead+"byteWrite="+byteWrite);}}
}

NIO核心之一Selector(选择器)

  1. 基本介绍:Selector能够检测多个注册的通道上是否是事件发生(注意:多个channel以事件的方式可以注册到同一个selector)。如果有事件发生,便获取事件然后针对每个事件进行相应的处理,这样就可以只有一个单线程去管理多个通道,也就是管理多个连接和请求。只有在 连接/通道 真正有读写事件发生时,才会读写,大大减少了系统开销,并且不必每一个连接都创建一个线程,不用去维护多个线程,减少了多线程之间的上下文切换导致的开销

  2. 相关方法

  3. 注意
    1.NIO中的ServerSocketChannel功能类似ServerSocket,SocketChannel功能类似Socket
    2.Selector.select()//阻塞,只有至少一个事件发生返回
    Selector.select(1000)//阻塞1000毫秒,在1000毫秒后返回,如果没有事件,也会返回
    Selector.wakeup()//唤醒selector阻塞时候使用
    Selector.selectNow()//不阻塞,立马返还

  4. Selector、SelectionKey、ServerScoketChannel和SocketChannel关系梳理图

    说明:1.当客户端连接时,会通过ServerSocketChannel得到对应的SocketChannel;2.将SocketChannel注册到Selector上,register(Selector sel,int ops),一个selector上可以注册多个socketChannel3.注册后返回一个SelectionKey,会和该Selector以集合的方式关联4.Sekector通过select()方法进行监听,会返回有事件产生的通道的个数5.进一步得到各个SelectorKey(事件发生的)6.再通过SelectorKey,反向获取socketChannel7.通过channel,完成业务处理
    
  5. 通过代码解读上面模型代码
    客户端

package com.dd.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;public class NIOClient {public static void main(String[] args) throws IOException {//得到一个网络通道SocketChannel socketChannel = SocketChannel.open();//设置非阻塞模式socketChannel.configureBlocking(false);//提供服务器端的ip和端口InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);//连接服务器if (!socketChannel.connect(inetSocketAddress)){while (!socketChannel.finishConnect()){System.out.println("因为连接需要时间,客户端不会阻塞,可以做其他工作");}}//如果连接成功就发送数据String srt ="hello word 哈塞给";//这个方法根据字节数组的大小生成buffer 相当于//ByteBuffer allocate = ByteBuffer.allocate(srt.length());//allocate.put(srt.getBytes());ByteBuffer buffer = ByteBuffer.wrap(srt.getBytes());//发送数据,将buffer数据写入到channelsocketChannel.write(buffer);//让代码停在这里System.in.read();}
}

服务端

package com.dd.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class NIOServer {public static void main(String[] args) throws IOException {//创建servserSocketChannel -> serverSocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//创建一个sekector对象Selector selector = Selector.open();//绑定端口serverSocketChannel.socket().bind(new InetSocketAddress(6666));//设置为非阻塞模式serverSocketChannel.configureBlocking(false);//把serverSocketChannel 注册到 selector 上       关心事件为op_acceptserverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//循环等待客户端连接while(true){//这里的等待一秒,如果没有事件发生就继续if (selector.select(1000) == 0){//没有事件发生System.out.println("服务器等待一秒,无连接");continue;}//如果返回>0,获取到相关的selectionKey集合//通过这个方法返回关注事件的集合,然后反向获取通道Set<SelectionKey> selectionKeys = selector.selectedKeys();//遍历Iterator<SelectionKey> Keyiterator = selectionKeys.iterator();while (Keyiterator.hasNext()){SelectionKey key = Keyiterator.next();//根据key 对应通道发生事件做相应的处理if (key.isAcceptable()){//如果时OP_ACCEPT ,有新的客户端连接//该客户端分配一个SocketChannel//注意,accept()方法不是阻塞的吗?//其实BIO中accept阻塞是因为不知道客户端连接,而NIO是由事件驱动的,咱们上面已经判断是连接事件,所以并不会阻塞SocketChannel socketChannel = serverSocketChannel.accept();//将socketChannel设置成非阻塞socketChannel.configureBlocking(false);//将SocketChannel注册到 seletor上 , 关注事件为OP_READ//同时给socketChannel关联一个buffersocketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));//从2,3,4.。。。System.out.println("注册后的selectionKey 数量="+selector.keys().size());}if (key.isReadable()){//发生了OP_READ事件//通过key 反向获取到对应的channelSocketChannel channel = (SocketChannel)key.channel();//获取到该channel关联的bufferByteBuffer buffer = (ByteBuffer)key.attachment();channel.read(buffer);System.out.println("客户端发送的数据是:"+new String(buffer.array()));}//手动从集合中移除当前的selectionKey,防止重复操作Keyiterator.remove();}}}
}

SelectionKey说明

  1. SelectionKey表示Selector和网络通道的注册关系,共四种

亲!点击观看Netty框架学习下一篇文章_基于NIO事件驱动的群聊系统

Netty由浅到深_第三章_NIO模型3大组件详细介绍相关推荐

  1. python blp模型 估计_第三章 BLP模型(Bell-La Padula模型) | 学步园

    是对安全策略形式化的第一个数学模型,是一个状态机模型,用状态变量表示系统的安全状态,用状态转换规则来描述系统的变化过程. 一.模型的基本元素 模型定义了如下的集合: S={s1,s2,-,sn}主体的 ...

  2. Lync Server 2010的部署系列_第三章 证书、架构、DNS规划

    Lync Server 2010的部署系列_第三章 证书.架构.DNS规划 一.证书规划 组件 使用者名称 使用者备用名称条目/顺序 证书颁发机构 (CA) 备注 边缘外部接口 Sip.Giantha ...

  3. c语言中,x-y,'105',ab,7f8那个是正确的,C语言程序设计_第三章 数据.ppt

    C语言程序设计_第三章 数据 * 运算符功能 与运算量关系 要求运算量个数 要求运算量类型 运算符优先级别 结合方向 结果的类型 学习运算符应注意 * 基本算术运算符: + - * / % 结合方向: ...

  4. 管理系统中计算机应用课件,管理系统中计算机应用_第三章课件.ppt

    <管理系统中计算机应用_第三章课件.ppt>由会员分享,提供在线免费全文阅读可下载,此文档格式为ppt,更多相关<管理系统中计算机应用_第三章课件.ppt>文档请在天天文库搜索 ...

  5. OpenCV函数简记_第三章数字图像的滤波处理(方框,均值,高斯,中值和双边滤波)

    系列文章目录 OpenCV函数简记_第一章数字图像的基本概念(邻域,连通,色彩空间) OpenCV函数简记_第二章数字图像的基本操作(图像读写,图像像素获取,图像ROI获取,图像混合,图形绘制) Op ...

  6. 《精通数据仓库设计》中英对照_第三章

    <精通数据仓库设计>中英对照_第三章 第二部分 模型开发 数据仓库应该表示企业数据的各个方面,这些方面以主题域和业务数据模型开始.我们将在第3章使用一个假想的公司,指导一步一步地开发这两个 ...

  7. ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区...

    原文:ArcGIS for Desktop入门教程_第三章_Desktop软件安装 - ArcGIS知乎-新一代ArcGIS问答社区 1 软件安装 1.1 安装前准备 请确认已经收到来自Esri中国( ...

  8. 灰度值取值范围_第三章 灰度变换与空间滤波-(三)直方图之直方图均衡

    听说:单反穷三代,摄影毁一生. 本节目录 3.1 直方图均衡 3.2 直方图匹配(规定化) 3.3 局部直方图处理 3.4 图像增强中使用直方图 真是感谢雷神,举例子还是以灰度图为主,减负了不少.是因 ...

  9. 屏幕输出语句_第三章 常用输入/输出函数

    第三章 常用输入/输出函数 与其他高级语言一样, C语言的语句是用来向计算机系统发出操作指令的. 当我们提到输入时,这意味着要向程序填充一些数据.输入可以是以文件的形式或从命令行中进行.C 语言提供了 ...

最新文章

  1. 六间房与花椒直播重组首次交割完成
  2. can总线rollingcounter_一文看懂CAN总线
  3. linux设备驱动学习(三)——并发控制
  4. Prime Distance POJ - 2689 线性筛
  5. Replace Type Code with Subclasses(以子类取代类型码)
  6. java zar_唬人的Java泛型并不难
  7. PHP笔记-随机生成cookie、后台检索、通过session获取ID增强安全性
  8. Mac 显示和隐藏文件
  9. laravel validate
  10. visio画图复制粘贴到word_用VISIO画图 复制完之后粘贴到word中为什么只显示下面一部分?...
  11. 计算机丢失boost,Win7系统安装后出现无法开启readyboost怎么办?
  12. 普通二极管伏安特性和肖特基二极管电压电流特性
  13. 快速分割多个视频,生成每个视频的m3u8
  14. 他们联手造了个抢票节”
  15. CSS 滚动条,浮动问题,图片bug
  16. 适配器模式 个人理解+JAVA 小例子
  17. iOS 更改webView文字颜色丶文字大小丶背景色的方法
  18. c语言stm8变量存储类型,STM8使用----STVD(COSMIC)定义变量指定其类型和位定义
  19. matlab毕达哥拉斯质数,这位天才发现了素数、完美数和亲和数,证明三角形内角和是180°...
  20. 现漏洞政府天价难护航

热门文章

  1. 图形学基础|屏幕空间反射(SSR)
  2. php jq跳转页面跳转,jquery中怎么跳转页面
  3. php excel 高度,PHPExcel:根据图像设置行高(以像素为单位)
  4. Unity 调用Java | 调用aar包 | 调用jar包 | Java内部类,附带实例,看我一篇就够啦~
  5. 一个换了马甲的OTL功放电路
  6. 【复杂句的逻辑练习题】定语从句的省略
  7. 压抑的胜利--张曼菱
  8. 自制操作系统-使用16进制文件显示 hello world
  9. 理解Project2007中的视图
  10. 数学建模历年真题分析