`
lovnet
  • 浏览: 6711551 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

NIO:Selector 详解

 
阅读更多

示例程序TCPEchoServerSelector中展示了Selector的基本用法。在此,我们将对其进行更加详细的介绍。

Selector:创建和关闭

static Selector open()

boolean isOpen()

void close()

调用Selectoropen()工厂方法可以创建一个选择器实例。选择器的状态是"打开""关闭"的。创建时选择器的状态是打开的,并保持该状态,直到调用close()方法通知系统其务已经完成。可以调用isOpen()方法来检查选择器是否已经关闭。

5.6.1在信道中注册

我们已经知道,每个选择器都有一组与之关联的信道,选择器对这些信道上"感兴趣的"I/O操作进行监听。SelectorChannel之间的关联由一个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_ ACCEPTOP_CONNECTOP_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个代表注册信道的键:两个在端口40004001上的服务器信道,以及从服务器信道创建的5个客户端信道:

SelectionKey:获取和取消

Selector selector()

SelectableChannel channel()

void cancel()

键关联的Selector实例和Channel实例可以分别使用该键的selector()channel()方法获得。cancel()方法用于(永久性地)注销该键,并将其放入选择器的注销集(canceled set中(图5.1)。在下一次调用select()方法时,这些键将从该选择器的所有键集中移除,其关联的信道也将不再被监听(除非它又重新注册)。

(点击查看大图)图5.1Selector与其关联的键集

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()方法是一个非阻塞版本:它总是立即返回,如果没有信道准备就绪,则返回0wakeup()方法可以使当前阻塞(也就是说在另一个线程中阻塞)的任何一种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中的选择器的已选键集中有两个键:K2K5

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()方法以位图的形式返回所有准备就绪的操作集。其他方法用于分别检查各种操作是否可用。

例如,查看键关联的信道上是否有正在等待的读操作,可以使用以下代码:

(key.readyOps() & SelectionKey.OP_READ) != 0key.isReadable()

选择器的已选键集中的键,以及每个键中准备就绪的操作,都是由select()方法来确定的。随着时间的推进,这些信息可能会过时。其他线程可能会处理准备就绪的I/O操作。同时,键也不是永远存在的。当其关联的信道或选择器关闭时,键也将失效。通过调用其cancel()方法可以显示地将键设置为无效。调用其isValid()方法可以检测一个键的有效性。无效的键将添加到选择器的注销键集中,并在下次调用任一种形式的select()方法或close()方法时从键集中移除。(当然,从键集中移除键意味着与它关联的信道也不再受监听。)

5.6.3信道附件

当一个信道准备好进行I/O操作时,通常还需要额外的信息来处理请求。例如,在前面的回显协议中,当客户端信道准备好写操作时,就需要有数据可写。当然,我们所需要的可写数据是由之前同一信道上的读操作收集的,但是在其可写之前,这些数据存放在什么地方呢?另一个例子是第3章中的成帧过程。如果一个消息一次传来了多个字节,我们需要保存已接收的部分消息,直到完整个消息接收完成。这两种情况都需要维护每个信道的状态信息。然而,我们非常幸运!SelectionKey通过使用附件使保存每个信道的状态变得容易。

SelectionKey:查找准备就绪的I/O操作

Object attach(Object ob)

Object attachment()

每个键可以有一个附件,数据类型只能是Object类。附件可以在信道第一次调用register()方法时与之关联,或者后来再使用attach()方法直接添加到键上。通过SelectionKeyattachment()方法可以访问键的附件。

5.6.4 Selector小结

总的来说,使用Selector的步骤如下:

I.创建一个Selector实例。

II.将其注册到各种信道,指定每个信道上感兴趣的I/O操作。

III.重复执行:

1.调用一种select方法。

2.获取选取的键列表。

3.对于已选键集中的每个键,

a.获取信道,并从键中获取附件(如果合适的话)

b.确定准备就绪的操作并执行。如果是accept操作,将接受的信道设置为非阻塞模式,

并将其与选择器注册。

c.如果需要,修改键的兴趣操作集

d.从已选键集中移除键

如果选择器告诉了你什么时候I/O操作准备就绪,你还需要非阻塞I/O吗?答案是肯定的。信道在已选键集中的键并不能确保非阻塞I/O,因为调用了select()方法后,键集信息可能会过时。另外,阻塞式写操作会阻塞等待直到写完所有的字节,而就绪集中的OP_WRITE仅表示至少有一个字节可写。实际上,只有非阻塞模式的信道才能与选择器进行注册:如果信道在阻塞模式,SelectableChannel类的register()方法将抛出IllegalBlockingModeException异常。

相关下载:

Java_TCPIP_Socket编程(doc)

http://download.csdn.net/detail/undoner/4940239

文献来源:

LSOFT.CN(琅软中国)

分享到:
评论

相关推荐

    Java NIO Selector用法详解【含多人聊天室实例】

    主要介绍了Java NIO Selector用法,结合实例形式分析了Java NIO Selector基本功能、原理与使用方法,并结合了多人聊天室实例加以详细说明,需要的朋友可以参考下

    Java NIO实战开发多人聊天室

    05-Java NIO-Channel-FileChannel详解(一).mp4 06-Java NIO-Channel-FileChannel详解(二).mp4 08-Java NIO-Channel-ServerSocketChannel.mp4 09-Java NIO-Channel-SocketChannel.mp4 10-Java NIO-Channel-...

    JAVA IO-NIO 详解

    NIO的核心组件包括Channel(通道)、Buffer(缓冲区)和Selector(选择器)。Channel是数据传输的通道,它替代了传统IO中的流;Buffer是数据的容器,它可以在Channel和程序之间进行数据的读写操作;Selector则用于...

    精通并发与netty 无加密视频

    第51讲:NIO零拷贝彻底分析与Gather操作在零拷贝中的作用详解 第52讲:NioEventLoopGroup源码分析与线程数设定 第53讲:Netty对Executor的实现机制源码分析 第54讲:Netty服务端初始化过程与反射在其中的应用分析...

    Java编程中的IO模型详解:BIO,NIO,AIO的区别与实际应用场景分析

    NIO是同步非阻塞模型,它借助selector能以一对多的方式处理连接,优点是连接数目较多且短的场景下效率较高,如聊天服务器,服务器间通信等,但编程相对复杂。AIO则是异步非阻塞模型,它由操作系统完成后回调通知...

    精通并发与 netty 视频教程(2018)视频教程

    39_NIO中Scattering与Gathering深度解析 40_Selector源码深入分析 41_NIO网络访问模式分析 42_NIO网络编程实例剖析 43_NIO网络编程深度解析 44_NIO网络客户端编写详解 45_深入探索Java字符集编解码 46_字符集编解码...

    精通并发与netty视频教程(2018)视频教程

    51_NIO零拷贝彻底分析与Gather操作在零拷贝中的作用详解 52_NioEventLoopGroup源码分析与线程数设定 53_Netty对Executor的实现机制源码分析 54_Netty服务端初始化过程与反射在其中的应用分析 55_Netty提供的Future与...

    Java CP/IP Socket编程

    5.6 Selector详解..........139 5.6.1 在信道中注册..........139 5.6.2 选取和识别准备就绪的信道..........141 5.6.3 信道附件..........143 5.6.4 Selector小结..........144 5.7 数据报(UDP)信道...........

    JAVA核心知识点整理(有效)

    1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................

Global site tag (gtag.js) - Google Analytics