NIO选择线程,按预期处理通道,但我如何确保通道在使用后正确关闭



所以我在我的ServerRunnable类中有以下代码:

public class FirmwareServerRunnable implements Runnable {
    private static Logger log = Logger.getLogger(FirmwareServerRunnable.class
            .getName());
    private LinkedTransferQueue<CommunicationState> communicationQueue;
    private int serverPort = 48485;
    public FirmwareServerRunnable(int port,
            LinkedTransferQueue<CommunicationState> communicationQueue) {
        serverPort = port;
        this.communicationQueue = communicationQueue;
    }
    private boolean running;
    private ServerSocketChannel serverSocketChannel;
    @Override
    public void run() {
        try {
            Selector selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(serverPort));
            log.info("Selector Thread: FirmwareServer Runnable- Listening for connections on port: "
                    + serverSocket.getLocalPort());
            running = true;
            @SuppressWarnings("unused")
            SelectionKey serverAcceptKey = serverSocketChannel.register(
                    selector, SelectionKey.OP_ACCEPT);
            while (running) {
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = (SelectionKey) keyIterator.next();
                    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
                        acceptConnection(selector, key);
                        keyIterator.remove();
                    } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                        CommunicationState commsState = (CommunicationState) key
                                .attachment();
                        if (commsState.getCurrentState() == CommunicationState.STATE_READ) {
                            readFromSocketChannel(key);
                            keyIterator.remove();
                        }
                    } else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
                        CommunicationState commsState = (CommunicationState) key
                                .attachment();
                        if (commsState.getCurrentState() == CommunicationState.STATE_WRITE) {

                            writeToSocketChannel(key);
                            keyIterator.remove();
                        }
                    }
                }
            }
        } catch (IOException e) {
            log.error(
                    "Firmware Selector Thread: An IOException occurred",
                    e);     
        }
    }

我的acceptConnection()方法接受一个连接,并添加一个CommunicationState对象(一个状态机),其中包含ByteBuffer,当前通道状态,客户端当前在通信过程中的位置等…该服务器在进程中在通信方法之间切换。最初,它使用JSON消息与客户端通信,但是当它达到一定程度时,它开始使用USART协议命令向客户端闪烁新固件。

一旦该过程完成,客户端断开连接并重新启动。这使我的频道处于未知状态。我不确定我这边的通道是否已经关闭。我怎么检查这个?我认为selector.selectedKeys()只返回准备操作的键是正确的吗?如果是这种情况,我如何检查没有正确关闭的连接?我可以在这个ServerRunnable while(running){}循环中做吗?

我一直在考虑的一个选项是将对密钥本身的引用附加到CommunicationState机,然后我可以在流程完成后获得对通道的引用并在那里关闭它。但出于某种原因,我对这个解决方案感到不安,我觉得它不对。

如果是这种情况,甚至包括封闭通道密钥,我可以使用key.isValid()来确认密钥需要永久删除吗?

我很感激你对这个过程的任何建议,我一定是忽略了什么。

编辑:一个快速的测试似乎表明,通道键不包括在选定的键集中,除非它们为三个定义的操作之一做好了准备

已被对等端关闭的连接将导致选择器将您的通道视为可读通道,并且当您从中读取时将得到-1,因此您应该关闭通道,这将取消其选择键。

编辑

如果是这种情况,甚至包括封闭的通道密钥,我可以使用key. isvalid()来确认密钥需要永久删除吗?

如果您关闭了通道,它的键将被取消,因此您将不会在下次的selected-keys集合中看到它。如果对端关闭了连接,参见上文。

最新更新