NIO介绍
NIO即no-blocking IO,表示同步非阻塞IO操作。采用多路复用器,事件驱动机制,当发生io请求时,触发相应的事件,多路复用器中的轮询器即selector轮询到事件,从而交由线程执行。请求发起方发起请求之后并不会被堵塞,可以设置为非堵塞的状态。
三大核心组件
java nio框架中,有三大核心的组件:Buffer、Channel、Selector。其关系图为:
Buffer
buffer即数据缓冲区,用于存储发送或读取Channel中的数据。数据流向是双向的,读写模式的切换需要依赖filp()方法。Buffer中重要的的几个属性。
- position: 表示当前读取或写入的索引位置。
- limit:表示当前读取或写入的限制索引位置。(position <= limit)
- capacity: 表示缓冲区的容量。
buffer中操作数据的方法有:get()
读取position位置的数据,put()
将数据写入position索引位置。array()
直接返回buffer中的数组(数组是final修饰的)。需要注意的是,buffer并不会真正删除数据,而是通过position,limit属性来控制读取和写入的位置,大小。例如,buffer数组初始化时,position为0,limit=capacity。当写入一个数据时,position+1。但需要读取时,如果不仅进行模式切换,此时将从position处开始读取,读取错误的信息。
filp方法是将limit = position ,postion=0等操作,从而限制只能读取写入的数据。
另一个方法clear()方法,将position = 0; limit = capactiy.
buffer的一些实现类如:ByteBuffer、CharBuffer、IntegerBuffer、LongBuffer。操作流程大同小异。
Channel
channel是连接selector的信道,传输信息数据,同时channel需要绑定到selector。
channel重要的种类有:
- FileChannel:操作文件
- SocketChannel:负责tcp的网络套接字
- ServerSockerChannel:负责tcp网络服务端。
- DatagramChannel:负责UDP通信
channel中的重要方法:
configBlocking(boolean)
: 配置该信道是否阻塞。register(Selector,Int)
用来绑定selector和事件。read(Buffer)
将channel信道中的数据读入到该buffer中
SelectionKey
如果说channel是一条管道,SelectionKey就是一个连接器。一个SelectionKey不仅将一个channel和selector绑定,还将channel触发事件绑定在selector中。SlectionKey中定义的几种事件常量:
- OP_READ:读事件。
- OP_WRITE::写事件。
- OP_CONNECT:套接字连接事件。
- OP_ACCEPT:套接字接收事件。
SelectionKey的几个重要方法:
cannel()
:返回该SelectionKey绑定的Channelselector()
:返回该SlectionKey绑定的SelectorisReadable():
判断是否为可读事件。isWritable():
判断是否为可写事件。isConnectable()
: 判断是否为连接事件。isAcceptable():
判断是否为接受事件。
Selector
轮询器。能够不断的轮询出已经触发事件的channel。Selector主要负责事件的轮询,保存并维护多个不同状态的SelectionKey数组。在本人有限的认知下,selector较重要的属性有:
protected Set<SelectionKey> selectedKeys
:存储已经触发过事件的SelectionKeyprotected HashSet<SelectionKey> keys;
:存储注册的SelectionKeyprivate Set<SelectionKey> publicKeys;
:存储当前触发事件的SelectionKeyprivate Set<SelectionKey> publicSelectedKeys;
:存储注册的SelectionKey
他们之间的关系为:
其中publicKeys是keys的不可修改集,publicSelectKeys是selectedKeys的不可成长集。这样做的深意我猜测是为了保护核心数据。
Selector常用的方法有:
selectedKeys()
:返回publicSelectedKeys数组,即当前触发事件的Key。kdys()
:返回当前publicSelectedKeys数组。select()
: 不断轮询数组,返回触发事件的key的个数。(会阻塞,直到出现触发事件)select(long)
: 不断轮询数组,返回触发事件的key的个数,(会阻塞,直到出现触发事件或long型的变量的时间到)selectNow()
: 轮询数组,返回触发事件的key的个数,(不会阻塞)
需要注意的是,在通过selectedKeys方法返回触发事件的keys数组中,需要使用iterator来进行key的遍历,切记:在处理完该事件之后需要调用iterator.remove()方法去删除selectedKeys中的对应key,否则,下次仍然会重复处理这个key。
使用案例
server端:
client端:
另外
从channel.register()的方法中看,channel在注册进selector中去的时候不仅将形成的selectionKey存进selector中,也将该selector存入进channel中的keys数组中去了。
``