您的位置:首页 > 编程语言 > Java开发

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了解到这些信息,但前提是将这个通道注册到了这个选择器。下面项目代码 展示的功能意义:
一个服务器,处理多个客户端的请求操作.
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();
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  NIO