java nio选择器未唤醒

watbbzwu  于 2021-06-30  发布在  Java
关注(0)|答案(0)|浏览(236)

有一些关于java非阻塞套接字的问题。
如果计时器线程中有5秒钟没有心跳消息,请拔下客户端的lan电缆并关闭客户端的socketchannel。

private void startSendHeartBeatMessage() {
    new Timer().scheduleAtFixedRate(new TimerTask() {
        @SneakyThrows
        @Override
        public void run() {
            if (connections.size() != 0) {
                LocalDateTime now = LocalDateTime.now();
                Iterator<MessageClient> iterator = connections.iterator();
                while (iterator.hasNext()) {
                    MessageClient client = iterator.next();
                    if (client.isClientAlive(now)) {
                        // got a heartbeat 5 seconds ago
                        client.setData("heartbeat".getBytes());
                        SelectionKey key = client.getSocketChannel().keyFor(selector);
                        key.interestOps(SelectionKey.OP_WRITE); // send heartbeat to client 
                    } else {
                        // remove client and close socketChannel
                        log.error("time out");
                        client.getSocketChannel().close();
                        iterator.remove();
                    }
                }
            }
            selector.wakeup();
        }
    }, 0, 1000);
}

当然,之所以称为channelclose,是因为局域网电缆被拔掉了。

private void startServer() {
    executorService.submit(() -> {
        try {
            while (true) {
                if (selector.select(1000) > 0) { // wait for event
                    Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                    while (keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();
                        keyIterator.remove();
                        if (key.isAcceptable()) {
                            accept(key);
                        } else if (key.isReadable()) {
                            MessageClient client = (MessageClient) key.attachment();
                            client.receive(key, selector);
                        } else if (key.isWritable()) {
                            MessageClient client = (MessageClient) key.attachment();
                            client.send(key, selector);
                        }
                    }
                }
            }
        } catch (IOException e) {
            stopServer();
        }
    });
    startSendHeartBeatMessage();
}

在客户的 socketChannel 关闭计时器 selector.wakeup() 以及 selector.select(1000) 不是唤醒阻塞,也没有捕捉到任何事件 SelectionKey 但是客户端打印出连接和消息传输是成功的。
经过检查,
KQueueSelectorImpl wakeup() 被称为 interruptTriggered 总是 true 当lan插头拔下,插座通道关闭时。

/**
 * KQueue based Selector implementation for macOS
 */
class KQueueSelectorImpl extends SelectorImpl {
    public Selector wakeup() {
        synchronized (interruptLock) {
            if (!interruptTriggered) {
                try {
                    IOUtil.write1(fd1, (byte)0);
                } catch (IOException ioe) {
                    throw new InternalError(ioe);
                }
                interruptTriggered = true;
            }
        }
        return this;
    }
}

不拔下lan电缆,通过cntr+c关闭客户端,可以非常正常地重新连接。
我真的不知道nio的内部工作结构。请帮帮我
执行环境运行在macbook和springboot2上。
谢谢你的阅读。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题