件。当事件从客户机处到来时, Selector 将它们多路分用并将这些事件分派到相应的 Channel 。
创建 Selector 最简单的办法是使用 open() 方法,如下所示: Selector selector = Selector.open();
通道遇上选择器
每个要为客户机请求提供服务的 Channel 都必须首先创建一个连接。下面的代码创建称为 Server 的 ServerSocketChannel 并将它绑定到本地端口: ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false);
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia, port ); serverChannel.socket().bind(isa);
每个要为客户机请求提供服务的 Channel 都必须接着将自己向 Selector 注册。 Channel 应根据它将处理的事件进行注册。例如,接受传入连接的 Channel 应这样注册,如下:
SelectionKey acceptKey =
channel.register( selector,SelectionKey.OP_ACCEPT);
Channel 向 Selector 的注册用 SelectionKey 对象表示。满足以下三个条件之一, Key 就失效:
Channel 被关闭。 ? Selector 被关闭。
? 通过调用 Key 的 cancel() 方法将 Key 本身取消。
?
Selector 在 select() 调用时阻塞。接着,它开始等待,直到建立了一个新的连接,或者另一个线程将它唤醒,或者另一个线程将原来的阻塞线程中断。 注册服务器
Server 是那个将自己向 Selector 注册以接受所有传入连接的 ServerSocketChannel ,如下所示:
SelectionKey acceptKey = serverChannel.register(sel, SelectionKey.OP_ACCEPT);
while (acceptKey.selector().select() > 0 ){
......
Server 被注册后,我们根据每个关键字(key)的类型以迭代方式对一组关键字进行处理。一个关键字被处理完成后,就都被从就绪关键字(ready keys)列表中除去,如下所示:
Set readyKeys = sel.selectedKeys();
Iterator it = readyKeys.iterator(); while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next(); it.remove(); .... .... .... }
如果关键字是可接受(acceptable)的,则接受连接,注册通道,以接受更多的事件(例如:读或写操作)。 如果关键字是可读的(readable)或可写的(writable),则服务器会指示它已经就绪于读写本端数据:
SocketChannel socket;
if (key.isAcceptable()) {
System.out.println(\
ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); socket = (SocketChannel) ssc.accept(); socket.configureBlocking(false); SelectionKey another =
socket.register(sel,SelectionKey.OP_READ|SelectionKey.OP_WRITE); }
if (key.isReadable()) {
System.out.println(\ String ret = readMessage(key); if (ret.length() > 0) {
writeMessage(socket,ret); } }
if (key.isWritable()) {
System.out.println(\ String ret = readMessage(key);
socket = (SocketChannel)key.channel();
if (result.length() > 0 ) { writeMessage(socket,ret); } } 回页首 唵嘛呢叭咪吽 — 非阻塞服务器套接字快显灵! 对 JDK 1.4 中的非阻塞 I/O 的介绍的最后一部分留给您:运行这个示例。 在这个简单的非阻塞服务器-套接字示例中,服务器读取发送自客户机的文件名,显示该文件的内容,然后将内容写回到客户机。 这里是您运行这个示例需要做的事情: 1. 安装 JDK 1.4(请参阅 参考资料)。 2. 将两个 源代码文件复制到您的目录。
3. 编译和运行服务器, java NonBlockingServer 。 4. 编译和运行客户机, java Client 。
5. 输入类文件所在目录的一个文本文件或 java 文件的名称。 6. 服务器将读取该文件并将其内容发送到客户机。
7. 客户机将把从服务器接收到的数据打印出来。(由于所用的 ByteBuffer
的限制,所以将只读取 1024 字节。) 8. 输入 quit 或 shutdown 命令关闭客户机。
回页首 结束语 Merlin 的新 I/O 包覆盖范围很广。Merlin 的新的非阻塞 I/O 实现的主要优点有两方面:线程不再在读或写时阻塞,以及 Selector 能够处理多个连接,从而大幅降低了服务器应用程序开销。 我们已经着重论述了新的 java.nio 包的这两大优点。我们希望,您将把在这里所学到的知识应用到自己的实际应用程序开发工作中