Java NIO知识总结
2018-02-27 21:28
211 查看
记博客的目的:知识只有发布,才会让自己记得更牢固,才便于自己回头来巩固提高!参考:https://www.cnblogs.com/shihuc/p/6559025.html
https://www.cnblogs.com/wangyang108/p/6031013.htmlNIO--new IO no BlockingIO ----非阻塞式IO----基于通道和缓冲区1:BUFFER缓冲区2:Channel 通道3:Selector----选择器优点1:能够进行数据的双向传输-----减少了流的数量,降低服务器的内存消耗。2. 数据存储在缓冲区。可以针对 缓冲区的数据 做定向操作!! 文件头,文件尾巴。大视频切成多个小视频,将一个视频文件切开后,没法播放。3.利用一个或者少量的服务器完成大量的用户的请求处理(一个服务端,多个客户端)------适用于短任务场景,比如聊天。
Channel的比重最大,NIO的功能主要基于Channel来实现,进行业务逻辑操作。
Selector主要是IO事件选择器,当一个Channel创建并配置好后,注册到Selector上,与Selector相关的重要概念是SelectionKey,这个上面绑定了IO事件相关的Channel。在获取到Channel后,进行数据的读写操作,Channel的数据读写是不能直接操作数据的,必须基于ByteBuffer进行,然而,Java NIO原生的ByteBuffer操作比较繁琐,要flip和clear操作。
1. 而我们在业务逻辑操作中,用到的channel,主要有ServerSocketChannel,SocketChannel
2. Selector,是事件选择器,创建Selector后,在调用select之前,在注册Channel到这个Selector上时,必须指定关注的事件类型(interestOps)。通过这个类的select函数,可以获取选择上监听到的IO事件。一旦select函数检测到事件,就可以从Selector上获取到具体有哪些IO事件,这些事件通过SelectionKey承载,SelectionKey上标记出该事件的类型,比如是OP_CONNECT,OP_ACCEPT还是OP_READ等。另外,SelectionKey还记录了对应该IO事件发生的Channel,可以通过SelectionKey得到该Channel。
3. ByteBuffer。 因为字节操作,是操作系统与IO设备之间进行通信的基本数据单元,在Java NIO中,各通道Channel之间进行数据通信时,指定必须是基于ByteBuffer的。 ByteBuffer有两个重要的函数,flip和clear。当Channel调用read函数,将数据读到ByteBuffer中后,ByteBuffer的数据长度指针将会移动到数据长度所在的位置,这个位置是小于等于ByteBuffer容量capacity值的。当业务逻辑操作读取到的数据前,需要对ByteBuffer做一下flip操作,就是将limit指针指向当前数据指针position的位置,然后,将position指针指向0的位置。数据逻辑结束后,一般要恢复ByteBuffer,即调用clear函数。
缓冲区: 在操作系统中缓冲区是为了解决CPU的计算速度和外设输入输出速度不匹配的问题,因为外设太慢了,如果没有缓冲区,那么CPU在外设输入的时候就要一直等着,就会造成CPU处理效率的低下,引入了缓冲之后,外设直接把数据放到缓冲中,当数据传输完成之后,给CPU一个中断信号,通知CPU:“我的数据传完了,你自己从缓冲里面去取吧”。如果是输出也是一样的道理。
通道: 那么通道用来做什么呢?其实从他的名字就可以看出,它就是一条通道,您想传递出去的数据被放置在缓冲区中,然后缓冲区中怎么从哪里传输出去呢?或者外设怎么把数据传输到缓冲中呢?这里就要用到通道。它可以进一步的减少CPU的干预,同时更有效率的提高整个系统的资源利用率,例如当CPU要完成一组相关的读操作时,只需要向I/O通道发送一条指令,以给出其要执行的通道程序的首地址和要访问的设备,通道执行通道程序便可以完成CPU指定的I/O任务。
选择器: 另外一项创新是选择器,当我们使用通道的时候也许通道没有准备好,或者有了新的请求过来,或者线程遇到了阻塞,而选择器恰恰可以帮助CPU了解到这些信息,但前提是将这个通道注册到了这个选择器。下面项目代码 展示的功能意义:
一个服务器,处理多个客户端的请求操作.
https://www.cnblogs.com/wangyang108/p/6031013.htmlNIO--new IO no BlockingIO ----非阻塞式IO----基于通道和缓冲区1:BUFFER缓冲区2:Channel 通道3:Selector----选择器优点1:能够进行数据的双向传输-----减少了流的数量,降低服务器的内存消耗。2. 数据存储在缓冲区。可以针对 缓冲区的数据 做定向操作!! 文件头,文件尾巴。大视频切成多个小视频,将一个视频文件切开后,没法播放。3.利用一个或者少量的服务器完成大量的用户的请求处理(一个服务端,多个客户端)------适用于短任务场景,比如聊天。
Channel的比重最大,NIO的功能主要基于Channel来实现,进行业务逻辑操作。
Selector主要是IO事件选择器,当一个Channel创建并配置好后,注册到Selector上,与Selector相关的重要概念是SelectionKey,这个上面绑定了IO事件相关的Channel。在获取到Channel后,进行数据的读写操作,Channel的数据读写是不能直接操作数据的,必须基于ByteBuffer进行,然而,Java NIO原生的ByteBuffer操作比较繁琐,要flip和clear操作。
1. 而我们在业务逻辑操作中,用到的channel,主要有ServerSocketChannel,SocketChannel
2. Selector,是事件选择器,创建Selector后,在调用select之前,在注册Channel到这个Selector上时,必须指定关注的事件类型(interestOps)。通过这个类的select函数,可以获取选择上监听到的IO事件。一旦select函数检测到事件,就可以从Selector上获取到具体有哪些IO事件,这些事件通过SelectionKey承载,SelectionKey上标记出该事件的类型,比如是OP_CONNECT,OP_ACCEPT还是OP_READ等。另外,SelectionKey还记录了对应该IO事件发生的Channel,可以通过SelectionKey得到该Channel。
3. ByteBuffer。 因为字节操作,是操作系统与IO设备之间进行通信的基本数据单元,在Java NIO中,各通道Channel之间进行数据通信时,指定必须是基于ByteBuffer的。 ByteBuffer有两个重要的函数,flip和clear。当Channel调用read函数,将数据读到ByteBuffer中后,ByteBuffer的数据长度指针将会移动到数据长度所在的位置,这个位置是小于等于ByteBuffer容量capacity值的。当业务逻辑操作读取到的数据前,需要对ByteBuffer做一下flip操作,就是将limit指针指向当前数据指针position的位置,然后,将position指针指向0的位置。数据逻辑结束后,一般要恢复ByteBuffer,即调用clear函数。
缓冲区: 在操作系统中缓冲区是为了解决CPU的计算速度和外设输入输出速度不匹配的问题,因为外设太慢了,如果没有缓冲区,那么CPU在外设输入的时候就要一直等着,就会造成CPU处理效率的低下,引入了缓冲之后,外设直接把数据放到缓冲中,当数据传输完成之后,给CPU一个中断信号,通知CPU:“我的数据传完了,你自己从缓冲里面去取吧”。如果是输出也是一样的道理。
通道: 那么通道用来做什么呢?其实从他的名字就可以看出,它就是一条通道,您想传递出去的数据被放置在缓冲区中,然后缓冲区中怎么从哪里传输出去呢?或者外设怎么把数据传输到缓冲中呢?这里就要用到通道。它可以进一步的减少CPU的干预,同时更有效率的提高整个系统的资源利用率,例如当CPU要完成一组相关的读操作时,只需要向I/O通道发送一条指令,以给出其要执行的通道程序的首地址和要访问的设备,通道执行通道程序便可以完成CPU指定的I/O任务。
选择器: 另外一项创新是选择器,当我们使用通道的时候也许通道没有准备好,或者有了新的请求过来,或者线程遇到了阻塞,而选择器恰恰可以帮助CPU了解到这些信息,但前提是将这个通道注册到了这个选择器。下面项目代码 展示的功能意义:
一个服务器,处理多个客户端的请求操作.
package com.edu.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class ServerDemo { public static void main(String[] args) throws IOException { //打开服务器端的通道 ServerSocketChannel ssc=ServerSocketChannel.open(); //绑定端口号 ssc.bind(new InetSocketAddress(8091)); // 将通道设置为非阻塞 ssc.configureBlocking(false); // 创建一个selector Selector selc = Selector.open(); //将创建的serverChannel注册到selector选择器上,指定这个channel只关心OP_ACCEPT事件 ssc.register(selc,SelectionKey.OP_ACCEPT); while(true){ /* * select()操作,默认是阻塞模式的,即,当没有read时间到来时,将一直阻塞不往下面继续执行。 * */ //进行选择 selc.select(); //将选择后事件获取出来 Set<SelectionKey> keys = selc.selectedKeys(); //获取到迭代器 Iterator<SelectionKey> it = keys.iterator(); while(it.hasNext()){ //获取事件 SelectionKey key = it.next(); //可能是一个接受事件 if(key.isAcceptable()){ //从事件中获取到通道 ServerSocketChannel sscx = (ServerSocketChannel)key.channel(); //接收连接 SocketChannel sc = sscx.accept(); while(sc==null){ sc=sscx.accept(); } sc.configureBlocking(false); sc.register(selc, SelectionKey.OP_READ|SelectionKey.OP_WRITE); } //可能是一个可读事件 if(key.isReadable()){ //获取通道 SocketChannel sc =(SocketChannel)key.channel(); // 准备缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); // 读取数据 sc.read(buffer); // 解析数据 buffer.flip(); System.out.println(new String(buffer.array(),0,buffer.limit())); sc.register(selc,key.interestOps() & ~SelectionKey.OP_READ); } //可能是一个可写事件 if(key.isWritable()){ // 从事件身上获取通道 SocketChannel sc = (SocketChannel) key.channel(); //写出数据 sc.write(ByteBuffer.wrap("读取成功~~~".getBytes())); sc.register(selc, key.interestOps()& ~SelectionKey.OP_WRITE); } it.remove(); } } } }
package com.edu.nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class ClientDemo { public static void main(String[] args) throws Exception{ // 创建一个通道 SocketChannel sc=SocketChannel.open(); //设置为 非阻塞 的 sc.configureBlocking(false); //将创建的channel连接到指定的服务地址 sc.connect(new InetSocketAddress("localhost", 8091)); //创建一个事件选择器 Selector Selector selc=Selector.open(); // 将创建的SocketChannel注册到指定的Selector上,并指定关注的事件类型为OP_CONNECT sc.register(selc,SelectionKey.OP_CONNECT); //无线循环体 while(true){ //监听创建的Selector选择器 selc.select(); //获取筛之后有用的事件 Set<SelectionKey> keys = selc.selectedKeys(); //获取迭代器 Iterator<SelectionKey> it = keys.iterator(); while(it.hasNext()){ //将遍历到的这个事件获取出来 SelectionKey key = it.next(); //向服务器发起连接 if(key.isConnectable()){ //从该事件获取对应的通道 SocketChannel scx=(SocketChannel) key.channel(); //判断连接是否成功 while(!scx.finishConnect()) ; scx.register(selc, SelectionKey.OP_WRITE|SelectionKey.OP_READ); } //向服务区写数据 if(key.isWritable()){ //从事件身上获取到通道 SocketChannel scx = (Socket 9d11 Channel) key.channel(); //写数据 scx.write(ByteBuffer.wrap("Hello".getBytes())); scx.register(selc, key.interestOps() & ~SelectionKey.OP_WRITE); } // 可能从服务器读取数据 if(key.isReadable()){ //从事件身上获取到通道 SocketChannel scx = (SocketChannel) key.channel(); //定义一个ByteBuffer的容器,容量为1k ByteBuffer buffer = ByteBuffer.allocate(1024); scx.read(buffer); buffer.flip(); System.out.println(new String(buffer.array(),0,buffer.limit())); scx.register(selc,key.interestOps() & ~SelectionKey.OP_READ); } it.remove(); } } } }
相关文章推荐
- 脚本编程基础知识点总结 推荐
- [转]WinForm开发,窗体显示和窗体传值相关知识总结
- Unity脚本-Rotate旋转相关知识总结
- C++总结 — C++知识导论图
- 【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine()
- Java 访问数据库 --java与DataBase相关知识总结(一)
- STL之容器、迭代器及算法知识总结
- 指针知识总结
- Java 访问数据库 --java与DataBase相关知识总结(四) java数据库连接池实现
- java提高篇(一)拓展篇 java知识汇总---IO流的使用规律总结(含代码示例)浅显易懂
- 初步总结javascript中学习DOM之前的知识
- 信息系统项目管理之项目范围管理知识总结
- Java 容器相关知识全面总结
- Servlet过滤器和监听器知识总结
- i2c知识总结及协议解析
- 【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法 分类: C# 2014-02-08 01:53 1826人阅读 评论(0) 收藏
- JAVA基础知识精华总结
- javaIO流、File文件知识总结
- Java基础知识强化之IO流笔记42:IO流总结(图解)
- 程序员自我修养第一章基础知识总结