java.nio.channels.Selector.select()立即返回0

时间:2018-05-16 09:47:35

标签: java udp nio multicast

以下代码打开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

当我运行另一个实例时出现问题。这个工作正常,但第一个实例开始立即报告超时,充斥其输出。此处的预期行为是,当第二个实例启动时,第一个和第二个实例都会收到一条消息,然后每十秒报告一次超时。我做错了什么?

1 个答案:

答案 0 :(得分:0)

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()

有关更多信息,请参见Why the key should be removed in `selector.selectedKeys().iterator()` in java nio?