为啥我会收到 CancelledKeyException?

Posted

技术标签:

【中文标题】为啥我会收到 CancelledKeyException?【英文标题】:Why do I get a CancelledKeyException?为什么我会收到 CancelledKeyException? 【发布时间】:2016-08-21 01:45:06 【问题描述】:

为什么我会收到 Canceled Key Exception 的错误消息?谁能帮帮我吗?客户端哪部分代码出错了?

java.nio.channels.CancelledKeyException
at sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:73)
at sun.nio.ch.SelectionKeyImpl.interestOps(SelectionKeyImpl.java:82)
at viaNIO.clientasync2.write(clientasync2.java:130)
at viaNIO.clientasync2.run(clientasync2.java:53)
at java.lang.Thread.run(Thread.java:745)

java.nio.channels.CancelledKeyException
at sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:73)
at sun.nio.ch.SelectionKeyImpl.readyOps(SelectionKeyImpl.java:87)
at java.nio.channels.SelectionKey.isReadable(SelectionKey.java:289)
at viaNIO.clientasync2.run(clientasync2.java:55)
at java.lang.Thread.run(Thread.java:745)

这是客户端的代码:

        while (!Thread.interrupted()) 

            selector.select();

            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

            while (keys.hasNext()) 
                SelectionKey key = keys.next();
                keys.remove();

                if (key.isValid()) 
                    if (key.isConnectable()) 
                        System.out.println("Connected to the server");
                        connect(key);
                    
                    if (key.isWritable()) 
                        System.out.println("Writing to the server");
                        write(key);
                    
                    if (key.isReadable()) 
                        System.out.println("Reading from the server");
                        read(key);
                    
                
            
        

这是更新后的代码:

   while (!Thread.interrupted()) 

            selector.select();

            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

            while (keys.hasNext()) 
                SelectionKey key = keys.next();
                keys.remove();

                if (key.isValid() && key.isConnectable()) 
                    connect(key);
                 else 
                    if (key.isValid() && key.isWritable()) 
                        System.out.println("Writing to the server");
                        write(key);
                    
                    if (key.isValid() && key.isReadable()) 
                        System.out.println("Reading from the server");
                        read(key);
                    
                
            
        

这也是我写方法的代码

 private void write(SelectionKey key) throws IOException 
    SocketChannel socket = (SocketChannel) key.channel();
    RandomAccessFile aFile = null;
    try 
        File f = new File("D:/test.rar");
        aFile = new RandomAccessFile(f, "r");
        ByteBuffer buffer = ByteBuffer.allocate(300000000);

        FileChannel inChannel = aFile.getChannel();
        while (inChannel.read(buffer) > 0) 
            buffer.flip();
            socket.write(buffer);
            buffer.clear();
        
        aFile.close();
        socket.close();
        inChannel.close();
        Thread.sleep(1000);

        key.interestOps(SelectionKey.OP_READ);
     catch (Exception e) 
        e.printStackTrace();
    

读取方法的代码

  private void read(SelectionKey key) throws IOException 
    SocketChannel channel = (SocketChannel) key.channel();
    ByteBuffer readBuffer = ByteBuffer.allocate(1000);
    readBuffer.clear();
    int length;
    try 
        length = channel.read(readBuffer);
     catch (IOException e) 
        System.out.println("Reading problem, closing connection");
        key.cancel();
        channel.close();
        return;
    
    if (length == -1) 
        System.out.println("Nothing was read from server");
        channel.close();
        key.cancel();
        return;
    
    readBuffer.flip();
    byte[] buff = new byte[1024];
    readBuffer.get(buff, 0, length);
    System.out.println("Server said: " + new String(buff));
    if (readCnt != 1) 
        key.interestOps(SelectionKey.OP_WRITE);
        readCnt++;
     else 
        key.cancel();
        close();
    

连接方法的代码

 private void connect(SelectionKey key) throws IOException 
    SocketChannel channel = (SocketChannel) key.channel();
    if (channel.isConnectionPending()) 
        channel.finishConnect();
    
    channel.configureBlocking(false);
    channel.register(selector, SelectionKey.OP_READ);

【问题讨论】:

【参考方案1】:

因为connect()write() 在您进行isReadable() 测试之前关闭了通道或取消了密钥。您需要继续重新测试isValid()。您还需要在 isConnectable() 块之后添加 else,因为 OP_CONNECT 不能与 OP_WRITE 或 OP_READ 一起触发:

if (key.isValid() && key.isConnectable()) 
    System.out.println("Connected to the server"); // see below
    connect(key);
 else 
    if (key.isValid() && key.isWritable()) 
        System.out.println("Writing to the server");
        write(key);
    
    if (key.isValid() && key.isReadable()) 
        System.out.println("Reading from the server");
        read(key);
    

另外,消息"Connected to the server" 放错了地方。您即将尝试完成连接:当且仅当finishConnect() 返回true 您实际已连接。

【讨论】:

我已经按照您建议的方式更改了我的代码,但我仍然在 sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:73) 处收到错误“java.nio.channels.CancelledKeyException” sun.nio.ch.SelectionKeyImpl.interestOps(SelectionKeyImpl.java:82) at viaNIO.clientasync2.write(clientasync2.java:128) at viaNIO.clientasync2.run(clientasync2.java:51) at java.lang.Thread.run (Thread.java:745)' 所以通道被关闭或者key被取消了。您必须发布新代码,包括 connect(), write()read() 方法。将其编辑到您的问题中。为什么要在write() 方法中再次检查interestOps 我已经编辑了我的帖子@EJP。你能帮忙看看我的代码哪里出错了吗?谢谢 正如我所说。您正在关闭write() 方法中的套接字,然后调用interestOps(),就好像它仍然处于打开状态一样。它不是。它开始没有意义。注意您在finishConnect() 周围的代码不正确。看我上面写的。 当且仅当 finishConnect() 返回true 通道已连接。您忽略了返回码。代码中的Thread.sleep() 调用毫无意义,clear() 应该是compact() NB 关闭频道会取消密钥。在close() 之后不需要cancel()

以上是关于为啥我会收到 CancelledKeyException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到此错误

为啥我会收到这个 TypeError?

为啥我会收到序列化错误?

为啥我会收到错误消息?

知道为啥我会收到此错误吗?

为啥我会收到这种错误验证错误?