JAVA NIO概述

NIO

java 1.4版本引入,给予缓冲区面 向通道的io操做java

bio nio
面向流 面向缓冲区(buffer)
阻塞io 非阻塞io
同步 同步
Selector(选择器)

缓冲区:是一个特定数据类型的容器,有java.nio 包定义,全部的缓冲区都是Buffer抽象类的子类
 子类:ByteBuffer  CharBuffer  ShortBuffer  IntBuffer LongBuffer FloatBuffer DoubleBuffer
 
 Buffer主要用于和NIO通道进行通讯,数据从通道读入到缓冲区,再从缓冲区读取到通道
 
 Buffer就像是一个数据能够保存多个类型相同的数据
 
 基本属性:
 1.容量(capacity):表示缓冲区的最大容量 一旦建立不能修改
 2.限制(limit):第一个不可读的索引,即位于limit后面的数据不可读
 3.位置(position):下一个要读取或写入数据的索引
 4.flip:将此时的position设为limit,position置为0 - 通常是从inputChannel将数据读入到buffer 而后将buffer flip后 为了从buffer中读取数据到 outputChannel 
 5.标记(mark)和恢复(reset):标记是一个索引,经过Buffer.mark()指定一个特定的位置,使用reset方法能够恢复到这个位置
  • 直接缓冲区:程序直接操做物理映射文件
  • 非直接缓冲区:jvm - 操做系统 - 物理内存

Channel:相似于流,可是Channel不能直接访问数据,只能与缓冲区进行交互通道

 主体实现类
 1.FileChannel:用于读取 写入 映射和操做文件的通道
 2.DataGramChannel:经过UDP读取网络中的数据通道
 3.SocketChannel:经过Tcp读写通道的数据
 4.ServerSocketChannel:能够监听新进入的Tcp链接,对每个新链接建立一个SocketChannel
 
 提供getChannel()方法的类
 1.FileInputStream 2.FileOutputStream 3.RandomAccessFile 4.Socket 5.ServerSocket 6.DataGramSocket
 
 通道时间传输
 1.transferFrom() 2.transferTo()
  • 分散读取(Scatter):将一个Channel 中的数据分散储存到多个Buffer
  • 汇集写入(Gather):将多个Buffer中的数据写入同一个Channel

Selector通常被称为选择器,也被称为多路复用器.用于检查一个或多个通道是否处于可读可写如此能够实现一个线程管理多个Channel
 
 使用Selector带来的好处有:使用更少的线程来处理Channel,能够防止上下文切换带来的性能小号
 
 能够被选择(多路复用)的Channel都继承自SelectableChannel
 
                             SelectableChannel 
                                    || 
                        AbstractSelectableChannel 
                      ||           ||            || 
            DataGramChannel SocketChannel ServerSocketChannel 
 
 因此FileChannel不适应与Selector,即不能切换为非阻塞模式
 Selector使用基本步骤
 1.建立Selector: Selector selector = Selector.open();
 2.设置为非阻塞为:channel.configureBlocking(false); 
 3.注册Channel到Selector:
 /** 
 * 参数-1:要注册到的多路复用器
 * 参数-2:是一个"interest集合",即要监听事件的集合(有如下四种)
 * OP_CONNECT 链接    
 * OP_ACEEPT 接收
 * OP_READ 读
 * OP_WRITE 写
 */ 
 SelectionKey key = channel.register(selector,SelectionKey.OP_READ); 
 若是要监听多种事件以下:
 SelectionKey key = channel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);    
 
 4.而后就 链接就绪 | 接收就绪 | 读就绪 | 写就绪

Selector主要方法

方法 描述
Set<SelectKey> keys() 返回全部SelectionKey集合,表明 注册在这个Selector上Channel
Set<SelectKey> selectedKeys() 返回已选择了的(即有io操做的)SelectionKey
int select() 监控全部注册了的Channel,若是有须要 io的操做时会将对应的selectKey加入到 selectedKeys集合中,返回的则是被选择 (有io操做的)Channel数量,这个操做时阻 塞的即只有被选择的Channel数量>=1才 返回
int select(timeout) 有超时时长,一直没有io操做的Channel出现, 到达timeout出现的时间后将自动返回
int selectNow() 无阻塞 当即返回
Selector wakeUp() 使正在select()当即返回
void close() 关闭

SelectionKey主要方法

SelectionKey表示ChannelSelector之间的关系,ChannelSelector注册就会产生一个SelectionKey缓存

方法 描述
int interestOps() 感兴趣事件的集合 boolean isInterested = interestSet & SelectionKey.OP_CONNECT ...
int readyOps() 获取通道准备好就绪的操做
SelectableChannel channel() 获取注册通道
Selector selector() 获取选择器
boolean isConnectable() 检测Channel中是否有链接事件就绪
boolean isAcceptable() 检测Channel中是否有接收事件就绪
boolean isReadaable() 检测Channel中是否有读事件就绪
boolean isWriteable() 检测Channel中是否有写事件就绪
Object attach() 将一个对象附着到SelectionKey上, 主要是一些用于标识的信息
Object attachment() 获取注册信息 也能够在Channel注册的时候附着信息 SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
void cancel() 请求取消此键的通道到其选择器的注册
package com.yuan.nio.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer {
    public static void main(String[] args) throws IOException {
         //建立服务端通道
         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
         //非阻塞模式
         serverSocketChannel.configureBlocking(false);
         //绑定端口
         serverSocketChannel.bind(new InetSocketAddress(9021));
         //建立选择器
         Selector selector = Selector.open();
         //注册 接收
         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
         //有一个事件时就操做
         while (selector.select() > 0) {
            //获取事件集合
             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
             while (iterator.hasNext()) {
                 SelectionKey selectionKey = iterator.next();
                 //若是是接收就绪
                 if (selectionKey.isAcceptable()) {
                     //获取客户端链接
                     SocketChannel socketChannel = serverSocketChannel.accept();
                     //切换成非阻塞
                     socketChannel.configureBlocking(false);
                     //注册在多路复用器上 读
                     socketChannel.register(selector, SelectionKey.OP_READ);
                     //读事件
                  } else if (selectionKey.isReadable()) {
                        //获取客户端链接
                         SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                         //设置缓存
                         ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                         int len = 0;
                         while (-1 != (len = socketChannel.read(byteBuffer))) {
                                 byteBuffer.flip();
                                 System.out.println(new String(byteBuffer.array(),0,len));
                                 byteBuffer.clear();
                         }
                        //请求取消此键的通道在其选择器的注册,也就是 selector.select();的数量 -1 selectionKey.cancel();
                        socketChannel.close();
                    }
              }
              iterator.remove();
         }
    }
}
相关文章
相关标签/搜索