3

NIO

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

bionio
面向流面向缓冲区(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();
         }
    }
}

eacape
205 声望8 粉丝

JAVA 攻城狮