java.nio.channels.Selector.select () немедленно возвращает 0

Следующий код открывает сокет UDP, устанавливает многоадресную рассылку, отправляет сообщение и запускает цикл чтения. Он использует Selector.select () для чтения с тайм-аутом.

int TIMEOUT = 10000;
String id = "8154@Think420";

try {
    NetworkInterface iface = NetworkInterface.getByInetAddress(InetAddress.getByName(address));
    try (DatagramChannel channel = DatagramChannel.open(StandardProtocolFamily.INET).setOption(StandardSocketOptions.SO_REUSEADDR, true)) {
        channel.socket().bind(new InetSocketAddress(PORT));
        channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, iface);
        channel.configureBlocking(false);
        InetAddress group = InetAddress.getByName("225.4.5.6");
        MembershipKey key = channel.join(group, iface);

        InetSocketAddress mcast = new InetSocketAddress(key.group(), PORT);
        channel.send(ByteBuffer.wrap(id.getBytes()), mcast);
        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_READ);

        ByteBuffer buffer = ByteBuffer.allocate(4096);
        while (key.isValid()) {
            if (selector.select(TIMEOUT) == 0) {
                System.err.println("timeout");
                continue;
            }

            buffer.clear();
            InetSocketAddress address = (InetSocketAddress) channel.receive(buffer);
            buffer.flip();
            String message = Charset.forName("ASCII").decode(buffer).toString();
            System.err.format("From %s received: %s\n", address.getHostString(), message);
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
} catch(UnknownHostException | SocketException e) {
    throw new IllegalArgumentException(e);
}

Когда я запускаю первый экземпляр приложения, он работает правильно: получает одно сообщение от себя, а затем выводит «тайм-аут» в цикле.

Вот пример вывода:

From 127.0.0.1 received: 8154@Think420
timeout
timeout
timeout

Проблема возникает, когда я запускаю другой экземпляр. Этот работает нормально, но первый экземпляр сразу начинает сообщать о тайм-аутах, переполняя свой вывод. Ожидаемое поведение здесь заключается в том, что при запуске второго экземпляра и первый, и второй получают одно сообщение, а затем сообщают о тайм-аутах каждые десять секунд. Что я делаю неправильно?


person Alexander Solovets    schedule 16.05.2018    source источник
comment
Действительно ли он сообщает о тайм-аутах сразу или после TIMEOUT?   -  person user207421    schedule 16.05.2018
comment
Он сообщает немедленно, как будто это while(true) System.out.println(...).   -  person Alexander Solovets    schedule 16.05.2018
comment
Что такое окружающая среда? Jdk 6-7? Linux? Если это так, похоже на ошибку epoll, найдите ошибку epoll селектора java и посмотрите на нее.   -  person vkx    schedule 17.05.2018
comment
Linux, JDK 1.8.   -  person Alexander Solovets    schedule 18.05.2018


Ответы (1)


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

if (selector.select(TIMEOUT) == 0) {
  System.err.println("timeout");
  continue;
}

buffer.clear();
InetSocketAddress address = (InetSocketAddress) channel.receive(buffer);
buffer.flip();
String message = Charset.forName("ASCII").decode(buffer).toString();
System.err.format("From %s received: %s\n", address.getHostString(), message);
selector.selectedKeys().clear()

См. Почему ключ должен быть удален в `selector.selectedKeys (). Iterator ()` в java nio? для получения дополнительной информации .

person Alexander Solovets    schedule 06.05.2019