使用 Selector 等待事件(例如使用 OP CONNECT)

NIO 出现在 Java 1.4 中并引入了 Channels 的概念,它应该比常规 I / O 更快。在网络方面, SelectableChannel 是最有趣的,因为它允许监视频道的不同状态。它的工作方式与 C select() 系统调用类似:当发生某些类型的事件时,我们会被唤醒:

  • 接收连接(OP_ACCEPT
  • 连接实现(OP_CONNECT
  • 读 FIFO 中可用的数据(OP_READ
  • 可以推送数据写入 FIFO(OP_WRITE

它允许在检测插槽 I / O(可以读/写/ …)和执行 I / O(读/写/ …)之间分离。特别是,所有 I / O 检测都可以在一个线程中为多个套接字(客户端)完成,而执行 I / O 可以在线程池或其他任何地方进行处理。这允许应用程序容易地扩展到连接的客户端的数量。

以下示例显示了基础知识:

  1. 创建一个 Selector
  2. 创建一个 SocketChannel
  3. 注册 SocketChannelSelector
  4. 使用 Selector 循环检测事件
Selector sel = Selector.open(); // Create the Selector
SocketChannel sc = SocketChannel.open(); // Create a SocketChannel
sc.configureBlocking(false); // ... non blocking
sc.setOption(StandardSocketOptions.SO_KEEPALIVE, true); // ... set some options

// Register the Channel to the Selector for wake-up on CONNECT event and use some description as an attachement
sc.register(sel, SelectionKey.OP_CONNECT, "Connection to google.com"); // Returns a SelectionKey: the association between the SocketChannel and the Selector
System.out.println("Initiating connection");
if (sc.connect(new InetSocketAddress("www.google.com", 80)))
    System.out.println("Connected"); // Connected right-away: nothing else to do
else {
    boolean exit = false;
    while (!exit) {
        if (sel.select(100) == 0) // Did something happen on some registered Channels during the last 100ms?
            continue; // No, wait some more
        
        // Something happened...
        Set<SelectionKey> keys = sel.selectedKeys(); // List of SelectionKeys on which some registered operation was triggered
        for (SelectionKey k : keys) {
            System.out.println("Checking "+k.attachment());
            if (k.isConnectable()) { // CONNECT event
                System.out.print("Connected through select() on "+k.channel()+" -> ");
                if (sc.finishConnect()) { // Finish connection process
                    System.out.println("done!");
                    k.interestOps(k.interestOps() & ~SelectionKey.OP_CONNECT); // We are already connected: remove interest in CONNECT event
                    exit = true;
                } else
                    System.out.println("unfinished...");
            }
            // TODO: else if (k.isReadable()) { ...
        }
        keys.clear(); // Have to clear the selected keys set once processed!
    }
}
System.out.print("Disconnecting ... ");
sc.shutdownOutput(); // Initiate graceful disconnection
// TODO: emtpy receive buffer
sc.close();
System.out.println("done");

会给出以下输出:

Initiating connection
Checking Connection to google.com
Connected through 'select()' on java.nio.channels.SocketChannel[connection-pending remote=www.google.com/216.58.208.228:80] -> done!
Disconnecting ... done