NIO Select
使用NIO的一个最大优势就是客户端于服务器自己的不再是阻塞式的,也就意味着服务器无需通过为每个客户端的链接而开启一个线程。而是通过一个叫Selector的轮循器来不断的检测那个Channel有消息处理。
简单来讲,Selector会不断地轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的Set集合,进行后续的I/O操作。
由于select操作只管对selectedKeys的集合进行添加而不负责移除,所以当某个消息被处理后我们需要从该集合里去掉。
NIO中的选择器(Selector)的作用就是维护注册到选择器中的通道集合,每一个通道与选择器的关系封装在选择键(SelectionKey)中,实际上可以认为选择器维护的是选择键集合。创建Selector对象使用Selector.open()。
Selector类主要维护三个集合:
- 注册到选择器中的SelectionKey集合
public abstract Set<SelectionKey> keys();
- 注册到选择器中已经准备好的SelectionKey集合
public abstract Set<SelectionKey> selectedKeys()
- 已被取消SelectionKey集合,但它们还没有被注销。cancelledKeys 是Selector对象的私有成员,外部无法访问
private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();
只有继承了SelectableChannel的类才能注册到选择器中,并且只有非阻塞模式的通道才能注册到选择器,如下代码:
//创建一个套接字服务器,并注册到选择器
//创建选择器
Selector selector = Selector.open();
//创建Socket服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定65535端口
ssc.socket().bind(new InetSocketAddress(65535));
//设置通道为非阻塞模式
ssc.configureBlocking(false);
//将通道注册到选择器,指定通道兴趣是等待接收连接
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
实际使用中,一般使用while循环轮询获取注册到选择器并且准备好的操作,如下代码:
while (true) {
int n = selector.select();
if (n > 0) {
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey keyy = iter.next();
iter.remove();
// ......
}
}
}
register方法
channel.register(selector, SelectionKey.OP_READ, buffer);
当我们调用该方法时,会将通道channel注册到选择器selector上,同时会返回一个SelectionKey选择键对象,这个键对象标识了通道和选择器之间的注册关系。选择键会记住您关心的通道。它们也会追踪对应的通道是否已经就绪。
注册一个通道时可以在这个选择键上设置一个Object对象,比如在接受连接操作中设置,在读操作中获取
SelectionKey keyy = iter.next();
iter.remove();
if(keyy.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) keyy.channel();
SocketChannel sc = channel.accept();
sc.register(selector, SelectionKey.OP_READ, "hello");
}
if(keyy.isReadable()){
//attach的值为"hello"
Object attach = keyy.attachment();
SocketChannel sc = (SocketChannel) keyy.channel();
}
select()方法
当调用一个选择器对象的select()方法时,相关的键建会被更新,用来检查所有被注册到该选择器的通道。您可以获取一个就绪键的集合,从而找到当时已经就绪的通道。通过遍历这些键,您可以选择出每个从上次您调用select( )开始直到现在,已经就绪的通道。
select()方法是阻塞的,直到有感兴趣的事件准备好;还可以使用select(10000)方法设置选择键的超时时间,单位Millisecond;还可以使用selectNow(),此方法是非阻塞,若没有通道就绪会立即返回0。
close()方法
不需要使用Selector时,调用close()方法可以关闭选择器,关闭选择器后,所有注册到其中的选择键会被设置为无效状态。
关闭选择器后,试图调用它的方法会抛出ClosedSelectorException,这是一个非运行时异常,所有在使用时有必要检查选择器是否打开,使用isOpen()方法。
Selector(选择器)提供了下面方法:
方法 | 介绍 |
open() | 打开一个选择器 |
isOpen() | 检查一个选择器实例是否打开 |
provider() | 返回一个SelectorProvider |
keys() | 返回注册键集合 |
selectedKeys() | 返回已选择键集合 |
selectNow() | 立刻执行选择,非阻塞,若没有已准备好的通道则立即返回0 |
select(long timeout) | 执行选择,超过指定毫秒数则返回 |
select() | 执行选择,会一直阻塞直到有准备就绪的通道 |
wakeup() | 停止选择 |
close() | 关闭选择器 |
下一篇: 基于select模型的server
推荐阅读
-
数据库SQL SELECT查询的工作原理
-
select count()和select count(1)的区别和执行方式讲解
-
jQuery获取Select选择的Text和Value详细解释
-
Vue.js 2.0中select级联下拉框实例
-
ERROR 1222 (21000): The used SELECT statements have a different number of column
-
select选择事件问题
-
jQuery实现select下拉框获取当前选中文本、值、索引
-
UCenter info: MySQL Query Error SQL:SELECT value FROM [Table]vars WHERE noteexis
-
jquery获取select选中值的文本,并赋值给另一个输入框的方法
-
关于Select Where In 的排序问题