Java NIO - Selector

  • 简述

    正如我们所知,Java NIO支持来自和指向通道和缓冲区的多个事务。因此,为了检查一个或多个NIO通道,并确定哪些通道已准备好进行数据事务即读取或写入Java,NIO提供了选择器。
    使用 Selector,我们可以创建一个线程来知道哪个通道已准备好进行数据写入和读取,并且可以处理该特定通道。
    我们可以通过调用其静态方法 open() 来获得选择器实例。打开选择器后,我们必须向它注册一个非阻塞模式通道,该通道返回选择键的实例。
    选择键基本上是可以使用通道执行的操作的集合,或者我们可以说我们可以在选择键的帮助下知道通道的状态。
    选择键表示的主要操作或通道状态为 −
    • 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(long timeout) − 此方法与 select() 相同,只是它阻塞线程的最大超时毫秒(参数)。
    • selectNow() − 此方法根本不阻塞。它会立即返回任何通道准备就绪。
    同样,为了留下一个调用选择方法的阻塞线程,可以从选择器实例调用wakeup()方法,之后在select()内等待的线程将立即返回。
    最后,我们可以通过调用 close() 方法来关闭选择器,该方法还会使注册到此选择器的所有 SelectKey 实例失效,同时关闭选择器。
  • 
    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<SelectionKey> selectedKeys = selector.selectedKeys();
             Iterator<SelectionKey> 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();
             }
          }
       }
    }