当前位置:编程学习 > 网站相关 >>

NIO:Selector 详解

示例程序TCPEchoServerSelector中展示了Selector的基本用法。在此,我们将对其进行更加详细的介绍。
 
Selector: 创建和关闭
 
static Selector open()
 
boolean isOpen()
 
void close()
 
调用Selector的open()工厂方法可以创建一个选择器实例。选择器的状态是"打开"或"关闭"的。创建时选择器的状态是打开的,并保持该状态,直到调用close()方法通知系统其务已经完成。可以调用isOpen()方法来检查选择器是否已经关闭。
 
5.6.1在信道中注册
 
我们已经知道,每个选择器都有一组与之关联的信道,选择器对这些信道上"感兴趣的"I/O操作进行监听。Selector与Channel之间的关联由一个SelectionKey实例表示。(注意一个信道可以注册多个Selector实例,因此可以有多个关联的SelectionKey实例)SelectionKey维护了一个信道上感兴趣的操作类型信息,并将这些信息存放在一个int型的位(bitmap)中,该int型数据的每一位都有相应的含义。
 
 SelectionKey类中的常量定义了信道上可能感兴趣的操作类型,每个这种常量都是只有一位设置为1的位掩码(bitmask)(见第3.1.3节) 
 
SelectionKey: 兴趣操作集
 
static int OP_ACCEPT
 
static int OP_CONNECT
 
static int OP_READ
 
static int OP_WRITE
 
int interestOps()
 
SelectionKey interestOps(int ops)
 
通过对OP_ ACCEPT,OP_CONNECT,OP_READ以及OP_WRITE中适当的常量进行按位OR,我们可以构造一个位向量来指定一组操作。例如,一个包含读和写的操作集由表达式(OP_READ | OP_WRITE)来指定。不带参数的interestOps()方法将返回一个int型位图,该位图中设置为1的每一位都指示了信道上需要监听的一种操作。另一个方法以一个位图为参数,指示了应该监听信道上的哪些操作。重点提示:任何对key(信道)所关联的兴趣操作集的改变,都只在下次调用了select()方法后才会生效。
 
SocketChannel, Server SocketChannel:注册Selector
 
SelectionKey register(Selector sel, int ops)
 
SelectionKey register(Selector sel, int ops, Object
 
attachment)
 
int validOps()
 
boolean isRegistered()
 
SelectionKey keyFor(Selector sel)
 
调用信道的register()方法可以将一个选择器注册到该信道。在注册过程中,通过存储在int型数据中的位图来指定该信道上的初始兴趣操作集(见上文的"SelectionKey:兴趣操作集")。register()方法将返回一个代表了信道和给定选择器之间的关联的SelectionKey实例。validOps()方法用于返回一个指示了该信道上的有效I/O操作集的位图。对于ServerSocketChannel来说,accept是惟一的有效操作,而对于SocketChannel来说,有效操作包括读、写和连接。对于DatagramChannel,只有读写操作是有效的。一个信道可能只与一个选择器注册一次,因此后续对register()方法的调用只是简单地更新该key所关联的兴趣操作集。使用isRegistered()方法可以检查信道是否已经注册了选择器。keyFor()方法与第一次调用register()方法返回的是同一个SelectionKey实例,除非该信道没有注册给定的选择器。
 
以下代码注册了一个信道,支持读和写操作:
 
SelectionKey key = clientChannel.register(selector,
 
SelectionKey.OP_READ | SelectionKey.OP_WRITE);
 
图5.1展示了一个选择器,其键集中包含了7个代表注册信道的键:两个在端口4000和4001上的服务器信道,以及从服务器信道创建的5个客户端信道:
 
SelectionKey: 获取和取消
 
Selector selector()
 
SelectableChannel channel()
 
void cancel()
 
键关联的Selector实例和Channel实例可以分别使用该键的selector()和channel()方法获得。cancel()方法用于(永久性地)注销该键,并将其放入选择器的注销集(canceled set)中(图5.1)。在下一次调用select()方法时,这些键将从该选择器的所有键集中移除,其关联的信道也将不再被监听(除非它又重新注册)。
 
 
 
(点击查看大图)图5.1:Selector与其关联的键集
 
Selected Key Set:   选择键集; Cancelled Key Set:注销键集; Key Set:键集;Interest Sets:
 
兴趣操作集
 
 5.6.2选取和识别准备就绪的信道
 
 在信道上注册了选择器,并由关联的键指定了感兴趣的I/O操作集后,我们就只需要坐下来等待I/O了。这要使用选择器来完成。
 
Selector: 等待信道准备就绪
 
int select()
 
int select(long timeout)
 
int selectNow()
 
Selector wakeup()
 
select()方法用于从已经注册的信道中返回在感兴趣的I/O操作集上准备就绪的信道总数。(例如,兴趣操作集中包含OP_READ的信道有数据可读,或包含OP_ACCEPT的信道有连接请求待接受。)以上三个select方法的惟一区别在于它们的阻塞行为。无参数的select方易做图阻塞等待,直到至少有一个注册信道中有感兴趣的操作准备就绪,或有别的线程调用了该选择器的wakeup()方法(这种情况下select方法将返回0)。以超时时长作为参数的select方法也会阻塞等待,直到至少有一个信道准备就绪,或等待时间超过了指定的毫秒数(正数),或者有另一个线程调用其wakeup()方法。selectNow()方法是一个非阻塞版本:它总是立即返回,如果没有信道准备就绪,则返回0。wakeup()方法可以使当前阻塞(也就是说在另一个线程中阻塞)的任何一种select方法立即返回;如果当前没有select方法阻塞,下一次调用这三种方法的任何一个都将立即返回。
 
 选择之后,我们需要知道哪些信道准备好了特定的I/O操作。每个选择器都维护了一个已选键集(selected-key set),与这些键关联的信道都有即将发生的特定I/O操作。通过调用selectedKeys()方法可以访问已选键集,该方法返回一组SelectionKey。我们可以在这组键上进行迭代,分别处理等待在每个键关联的信道上的I/O操作。
 
Iterator<SelectionKey> keyIter =
 
selector.selectedKeys().iterator();
 
while (keyIter.hasNext()) {
 
SelectionKey key = keyIter.next();
 
// ...Handle I/O for key's channel...
 
keyIter.remove();
 
}
 
 图5.1中的选择器的已选键集中有两个键:K2和K5。
 
Selector: 获取键集
 
Set<SelectionKey> keys()
 
Set<SelectionKey> selectedKeys() 
 
以上方法返回选择器的不同键集。keys()方法返回当前已注册的所有键。返回的键集是不可修改的:任何对其进行直接修改的尝试(如,调用其remove()方法)都将抛出UnsupportedOperationException异常。selectedKeys()方法用于返回上次调用select()方法时,被"选中"的已准备好进行I/O操作的键。重要提示:selectedKeys()方法返回的键集是可修改的,实际上在两次调用select()方法之间,都必须"手工"将其清空。换句话说,select方法只会在已有的所选键集上添加键,它们不会创建新的键集。
 
所选键集指示了哪些信道当前可以进行I/O操作。对于选中的每个信道,我们需要知道它们各自准备好的特定I/O操作。除了兴趣操作集外,每个键还维护了一个即将进行的I/O操作集,称为就绪操作集(ready set)。 
 
SelectionKey: 查找就绪的I/O操作
 
int readyOps()
 
boolean isAcceptable()
 
boolean isConnectable()
 
boolean isReadable()
 
boolean isValid()
 
boolean isWritable()
 
对于给定的键,可以使用readyOps()方法或其他指示方法来确定兴趣集中的哪些I/O操作可以执行。readyOps()方法以位图的形式返回所有准备就绪的操作集。其他方法用于分别检查各种操作是否可用。 
 
例如,查看键关联的信道上是否有正在等待的读操作,可以使用以下代码:
 
(k
补充:综合编程 , 其他综合 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,