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

Java NIO选择器线程,按预期处理通道,但如何确保通道在使用后正确关闭?,java,multithreading,server,nio,Java,Multithreading,Server,Nio,因此,我的ServerRunnable类中有以下代码: public class FirmwareServerRunnable implements Runnable { private static Logger log = Logger.getLogger(FirmwareServerRunnable.class .getName()); private LinkedTransferQueue<CommunicationState> co

因此,我的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);     
        }

    }
公共类FirmwareServerRunnable实现Runnable{
私有静态记录器log=Logger.getLogger(FirmwareServerRunnable.class
.getName());
专用链接传输队列通信队列;
专用int服务器端口=48485;
公共FirmwareServerRunnable(内部端口,
LinkedTransferQueue通信队列){
服务器端口=端口;
this.communicationQueue=通信队列;
}
私有布尔运行;
专用服务器socketchannel服务器socketchannel;
@凌驾
公开募捐{
试一试{
选择器=选择器。打开();
serverSocketChannel=serverSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket ServerSocket=serverSocketChannel.socket();
bind(新的InetSocketAddress(serverPort));
log.info(“选择器线程:固件服务器可运行-侦听端口上的连接:”
+serverSocket.getLocalPort());
运行=真;
@抑制警告(“未使用”)
SelectionKey serverAcceptKey=serverSocketChannel.register(
选择器,SelectionKey.OP_ACCEPT);
(跑步时){
selector.select();
设置selectedKeys=选择器。selectedKeys();
迭代器keyIterator=selectedKeys.Iterator();
while(keyIterator.hasNext()){
SelectionKey=(SelectionKey)keyIterator.next();
if((key.readyOps()&SelectionKey.OP_ACCEPT)==SelectionKey.OP_ACCEPT){
接受连接(选择器、键);
keyterator.remove();
}else if((key.readyOps()&SelectionKey.OP\u READ)==SelectionKey.OP\u READ){
通讯状态通讯状态=(通讯状态)键
.附件();
if(commsState.getCurrentState()==CommunicationState.STATE\u READ){
从插座通道(键)读取;
keyterator.remove();
}
}else if((key.readyOps()&SelectionKey.OP_WRITE)==SelectionKey.OP_WRITE){
通讯状态通讯状态=(通讯状态)键
.附件();
if(commsState.getCurrentState()==CommunicationState.STATE\u WRITE){
写入socketchannel(键);
keyterator.remove();
}
}
}
}
}捕获(IOE异常){
日志错误(
“固件选择器线程:发生IOException”,
e) );
}
}
我的
acceptConnection()
方法接受一个连接,并向其中添加一个
CommunicationState
对象(状态机),该对象包含
ByteBuffer
、当前通道状态、客户端当前在通信过程中的位置等内容。
此服务器在进程中间在通信方法之间切换。最初它使用JSON消息与客户端通信,但当到达某一点时,它开始使用新固件刷新客户端

进程完成后,客户端将断开连接并重新启动。这会使我的通道处于未知状态。我不确定通道是否已关闭。 我该如何检查?我认为
selector.selectedKeys()
只返回可供操作的键是否正确?如果是这种情况,我该如何检查尚未正确关闭的连接?我可以在(运行){}循环中执行此操作吗

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

如果甚至包括闭合通道密钥,我是否可以使用
key.isValid()
来确认需要永久删除密钥

我很感激你对这个过程的任何想法,我一定忽略了什么

编辑:快速测试似乎表明通道关键点不包括在所选关键点集中,除非它们已准备好进行三个定义操作之一
我的测试不好。

对等方关闭的连接将导致选择器将您的频道视为可读,当您从中读取时,将得到-1,此时您应关闭频道,这将取消其选择键

编辑

如果在这种情况下,即使包含闭合通道密钥,我是否可以使用key.isValid()来确认需要永久删除密钥


如果您关闭了频道,其密钥将被取消,因此下次您将无法在所选密钥集中看到它。如果对等方关闭了连接,请参见上文。

好的,谢谢,my
readFromSocketChannel()
方法处理该问题。这会引发其他问题。显然,在客户端断开连接后,我在某个地方有一个循环没有正确退出。当然是这样,请重新编辑。选定的密钥集是为其注册的操作选择的密钥集。是的,我知道,我在抓救命稻草!:-)返回methodical方法就是这样。我将再次尝试一步一步地检查代码。我遇到了这样一种情况,流程正在完成,客户端成功重新启动,但我机器上的处理器保持在30%-40%之间,就好像它是cont