📜  Java NIO-选择器

📅  最后修改于: 2020-11-14 10:14:27             🧑  作者: Mango


众所周知,Java NIO支持通道和缓冲区之间的多个事务,因此为了检查一个或多个NIO通道,并确定哪些通道已准备好进行数据事务,即读取或写入Java NIO提供了选择器。

使用Selector,我们可以使线程知道哪个通道已准备好进行数据写入和读取,并且可以处理该特定通道。

我们可以通过调用其静态方法open()来获取选择器实例。打开选择器之后,我们必须向其注册一个非阻塞模式通道,该通道返回SelectionKey的实例。

SelectionKey基本上是可以通过channel执行的操作的集合,或者我们可以说我们可以借助选择键来了解Channel的状态。

选择键代表的主要操作或通道状态为-

  • SelectionKey.OP_CONNECT-准备连接到服务器的通道。

  • SelectionKey.OP_ACCEPT-准备接受传入连接的通道。

  • SelectionKey.OP_READ-准备读取数据的通道。

  • SelectionKey.OP_WRITE-准备好数据写入的通道。

注册后获得的选择密钥具有以下一些重要方法-

  • attach() -此方法用于通过键附加对象。将对象附加到通道的主要目的是识别相同的通道。

  • attachment() -此方法用于保留通道中的附加对象。

  • channel() -此方法用于获取为其创建特定键的通道。

  • selector() -此方法用于获取为其创建特定键的选择器。

  • isValid() -此方法返回密钥是否有效的天气。

  • isReadable() -此方法指出天气密钥的通道是否可以读取。

  • isWritable() -此方法声明天气密钥的通道是否可以写。

  • isAcceptable() -此方法指出天气密钥的通道是否已准备好接受传入的连接。

  • isConnectable() -此方法测试此密钥的通道是否已完成其套接字连接操作或未能完成其套接字连接操作。

  • isAcceptable() -此方法测试此密钥的通道是否准备好接受新的套接字连接。

  • interestOps() -此方法检索此密钥的兴趣集。

  • readyOps() -此方法检索就绪集,即通道准备进行的一组操作。

我们可以通过调用其静态方法select()从选择器中选择一个通道。选择器的select方法被重载为-

  • select() -此方法阻塞当前线程,直到至少一个通道准备好为其注册的事件为止。

  • select(long timeout) -此方法与select()相同,除了它在最大超时毫秒(参数)内阻塞线程。

  • selectNow() -此方法完全不会阻塞,无论通道准备就绪都会立即返回。

同样,为了留下一个阻塞的线程来调用select方法,可以从选择器实例中调用wakeup()方法,然后在select()内部等待的线程将立即返回。

最后,我们可以通过调用close()方法来关闭选择器,该方法还将使与此选择器注册的所有SelectionKey实例无效,同时关闭选择器。

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SelectorDemo {
   public static void main(String[] args) throws IOException {
      String demo_text = "This is a demo String";    
      Selector selector = Selector.open();
      ServerSocketChannel serverSocket = ServerSocketChannel.open();
      serverSocket.bind(new InetSocketAddress("localhost", 5454));
      serverSocket.configureBlocking(false);
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      ByteBuffer buffer = ByteBuffer.allocate(256);
      while (true) {
         selector.select();
         Set selectedKeys = selector.selectedKeys();
         Iterator iter = selectedKeys.iterator();
         while (iter.hasNext()) {
            SelectionKey key = iter.next();
            int interestOps = key.interestOps();
            System.out.println(interestOps);
            if (key.isAcceptable()) {
               SocketChannel client = serverSocket.accept();
               client.configureBlocking(false);
               client.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
               SocketChannel client = (SocketChannel) key.channel();
               client.read(buffer);
               if (new String(buffer.array()).trim().equals(demo_text)) {
                  client.close();
                  System.out.println("Not accepting client messages anymore");
               }
               buffer.flip();
               client.write(buffer);
               buffer.clear();
            }
            iter.remove();
         }
      }
   }
}