Java nio select() не блокирует поток

183
28 мая 2018, 23:40

Есть сервер (однопоточный 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() остается блокирующим). В чем может быть проблема?

Answer 1

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

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

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

READ ALSO
Одинаковые слова в предложении

Одинаковые слова в предложении

Как найти одинаковые слова в предложении и вывести эти предложения? Есть идея вначале, получить предложения:

185
Как получить список компаний Github API?

Как получить список компаний Github API?

Нужно вывести список самых популярных компаний по языкам программированияНе понимаю как отделить компании по языкам

192