Java NIO select() возвращает без выбранных ключей – почему?

StackOverflow https://stackoverflow.com/questions/204186

  •  03-07-2019
  •  | 
  •  

Вопрос

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

SelectionKey.OP_READ | SelectionKey.OP_CONNECT

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

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

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

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

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

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

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

Это было полезно?

Решение

Короткий ответ:удалять 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.

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top