0

Есть сервер (однопоточный nio) работающий с клиентами, все работает корректно кроме того, что после первого ответа клиенту метод select перестает блокировать поток (в котором был вызван), но все продолжает работать, а процессор загружен на максимум (причем при дебаге все работает).

Создается ServerSocketChannel так:

Selector selector = SelectorProvider.provider().openSelector();
ServerSocketChannel serverChanel = ServerSocketChannel.open();
serverChanel.configureBlocking(false);
serverChanel.register(selector, serverChanel.validOps());
serverChanel.bind(new InetSocketAddress(lisenHost, listenPort));

while(true) {

selector.select(); //С какого-то момента не блокирует и как правило возвращает 0

Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
                logger.debug(selectionKeys.hasNext());
...

Присоединение клиента происходит так

SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
socketChannel.configureBlocking(false);
socketChannel.register(key.selector(), SelectionKey.OP_READ);

Если сам сервер куда-то конектится это происходит так:

SocketChannel hostSocket = SocketChannel.open();
hostSocket.configureBlocking(false);
hostSocket.register(key.selector(), SelectionKey.OP_CONNECT);
hostSocket.connect(new InetSocketAddress(address, port));

...

if (key.isConnectable()) {
((SocketChannel) key.channel()).finishConnect();
key.interestOps(SelectionKey.OP_READ);
}

Когда надо что-то записать слушаю SelectionKey.OP_READ | SelectionKey.OP_WRITE.

После записи выполняю key.interestOps(SelectionKey.OP_READ) И после выполнения операции на запись select() перестает блокировать (если заменить key.interestOps(SelectionKey.OP_READ) на key.interestOps(0), то select() остается блокирующим). В чем может быть проблема?

Andrey
  • 397
  • Покажите код регистрации в селекторе принятых соединений. – Sergey Gornostaev May 19 '18 at 13:11
  • @SergeyGornostaev Вопрос по поводу вашего ответа. Что если я хочу продолжать использовать ключ, а не закрывать его key.cancel()? – Andrey May 19 '18 at 16:37
  • По идее, должно работать key.interestOps(SelectionKey.OP_READ) или key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE). – Sergey Gornostaev May 19 '18 at 16:50
  • @SergeyGornostaev Я так и делаю key.interestOps(SelectionKey.OP_READ) – Andrey May 19 '18 at 18:39
  • Наверняка есть какой-то неявный мешающий фактор. Боюсь, что обнаружить его можно только прогнав программу под отладчиком и посмотрев, какие именно каналы и в каком именно состоянии попадают в селектор, когда он перестаёт блокироваться. – Sergey Gornostaev May 19 '18 at 18:43
  • @SergeyGornostaev Под отладкой как выяснилось все работает (хоть программа и однопоточная) – Andrey May 19 '18 at 18:47
  • Гейзенбаг, однако! – Sergey Gornostaev May 19 '18 at 18:57
  • @SergeyGornostaev Если после в методе write добавить Thread.sleep(100) то все работает. – Andrey May 19 '18 at 19:13
  • 1
    @SergeyGornostaev Все работает, баг состоял в том что я пытался писать в еще не законекченный канал – Andrey May 20 '18 at 10:06

1 Answers1

1

select() возвращает 0 в том случае, когда зарегистрированные в селекторе сокеты перешли в состояние готовности ещё до вызова select().

Скорее всего проблема связана с тем, что сокеты всегда готовы к записи, если ваш сервер не генерирует поток данных полностью исчерпывающий пропускную способность сетевого интерфейса. Поэтому регистрировать сокет с OP_WRITE стоит только тогда, когда вам есть, что в него писать, а после вызывать key.cancel().

И небольшая рекомендация: Поменяйте порядок следования вызовов bind и register. Регистрировать в селекторе стоит только уже открытые соединения.