Java NIO select () возвращает без выбранных ключей - почему? - PullRequest
7 голосов
/ 15 октября 2008

При написании тестового кода я обнаружил, что Selector.select () может вернуться без Selector.selectedKeys (), содержащего какие-либо ключи для обработки. Это происходит в тесном цикле, когда я регистрирую канал accept () с помощью

SelectionKey.OP_READ | SelectionKey.OP_CONNECT

как интересующие операции.

Согласно документации, select () должен возвращаться, когда:

1) Есть каналы, на которые можно воздействовать.

2) Вы явно вызываете Selector.wakeup () - ключи не выбраны.

3) Вы явно используете Thread.interrupt () для потока, выполняющего select () - ключи не выбраны.

Если я не получаю ключей после выбора (), я должен быть в случаях (2) и (3). Однако мой код не вызывает wakeup () или interrupt (), чтобы инициировать эти возвраты.

Есть идеи, что вызывает такое поведение?

Ответы [ 2 ]

9 голосов
/ 11 июля 2011

Причина в том, что OP_CONNECT и OP_WRITE - это одно и то же под капотом, поэтому вы никогда не должны регистрироваться для обоих одновременно (то же самое OP_ACCEPT и OP_READ), и вы никогда не должны регистрироваться для OP_CONNECT вообще, когда канал уже подключен, как это в данном случае принято.

И OP_WRITE почти всегда готов, за исключением случаев, когда буфер отправки сокета в ядре e заполнен, поэтому вы должны регистрироваться для этого только после получения записи нулевой длины. Таким образом, зарегистрировав уже подключенный канал на OP_CONNECT,, вы действительно зарегистрировались на OP_WRITE,, который был готов, поэтому select() сработал.

9 голосов
/ 15 октября 2008

Краткий ответ: удалите OP_CONNECT из списка интересующих вас операций для принятого соединения - принятое соединение уже подключено.

Мне удалось воспроизвести проблему, которая может быть именно тем, что с вами происходит:

import java.net.*;
import java.nio.channels.*;


public class MyNioServer {
  public static void main(String[] params) throws Exception {
    final ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(true);
    serverChannel.socket().bind(new InetSocketAddress("localhost", 12345));
    System.out.println("Listening for incoming connections");
    final SocketChannel clientChannel = serverChannel.accept();
    System.out.println("Accepted connection: " + clientChannel);


    final Selector selector = Selector.open();
    clientChannel.configureBlocking(false);
    final SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT);
    System.out.println("Selecting...");
    System.out.println(selector.select());
    System.out.println(selector.selectedKeys().size());
    System.out.println(clientKey.readyOps());
  }
}

После того, как вышеуказанный сервер получает соединение, самый первый select() на соединении выходит без блокировки, и нет ключей с готовыми операциями. Я не знаю, почему Java ведет себя так, но, похоже, многие люди укушаются этим поведением.

Результат одинаков для Sun JVM 1.5.0_06 в Windows XP, а также для Sun JVM 1.5.0_05 и 1.4.2_04 в Linux 2.6.

...