Java Nio服务器中的CasteException:无法将SocketChannel强制转换为ServerSocketChannel

Java Nio服务器中的CasteException:无法将SocketChannel强制转换为ServerSocketChannel,java,nio,classcastexception,Java,Nio,Classcastexception,我首先使用伪代码来描述这个问题,然后将整个代码粘贴到下面的代码中,它可以在本地运行 1.selector = Selector.open(); serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(port), 1024); serverChannel.register

我首先使用伪代码来描述这个问题,然后将整个代码粘贴到下面的代码中,它可以在本地运行

1.selector = Selector.open();
  serverChannel = ServerSocketChannel.open();
  serverChannel.configureBlocking(false);
  serverChannel.socket().bind(new InetSocketAddress(port), 1024);
  serverChannel.register(selector, SelectionKey.OP_ACCEPT);

2.while(true){
   selector.select();
   Set<SelectionKey> keys = selector.selectedKeys();
   Iterator<SelectionKey> it = keys.iterator();
    while (it.hasNext()) {
     SelectionKey key = it.next();
3.   if(!key.isAcceptable()){
        continue;
     }
4.   ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
     ...
    }
  }
异常发生在步骤4中,然后我在步骤3中进行检查,但它无法通过可接受的检查并进入死循环。 有时候,它可以正常接收和响应,我没有做任何改变,这对我来说太奇怪了。 在这里我粘贴代码,希望有人能帮助我。谢谢

包io.Nio; 导入java.io.IOException; 导入java.net.InetSocketAddress; 导入java.nio.ByteBuffer; 导入java.nio.channels.ClosedSelectorException; 导入java.nio.channels.SelectionKey; 导入java.nio.channels.Selector; 导入java.nio.channels.ServerSocketChannel; 导入java.nio.channels.SocketChannel; 导入java.util.Iterator; 导入java.util.Set; 导入io.util.IOUtil; 公共类NioServer扩展线程{ 专用int端口; 专用选择器; 专用服务器socketchannel服务器通道; 公共NioServerint端口{ this.port=端口; } @凌驾 公开募捐{ 试一试{ 选择器=selector.open; serverChannel=ServerSocketChannel.open; serverChannel.configureBlockingfalse; serverChannel.socket.bindnew-InetSocketAddressport,1024; serverChannel.registerselector,选择key.OP_ACCEPT; }卡奇奥例外{ IOUtil.closeserverChannel; } System.out.printlnserver启动:+端口; whiletrue{ 试一试{ selector.select; }捕获关闭的选择异常e{ e、 打印跟踪; }捕捉异常{ e、 打印跟踪; } 设置键=选择器。选择键; 迭代器it=keys.Iterator; 当它有下一个{ 选择key=it.next; 如果!key.isValid{ 键。取消; IOUtil.closekey.channel; IOUtil.closekey.selector; System.out.printlnIOUtil.now+清除无效密钥。; 持续 } //我在这里做了一个检查,如果不可接受,那么继续,但这是一个死循环 如果!key.isAcceptable{ System.out.PrintLn不可接受; 持续 } 试一试{ //此处的例外情况:无法将SocketChannel强制转换为服务器SocketChannel ServerSocketChannel serverChannel=ServerSocketChannel key.channel; SocketChannel通道=serverChannel.accept; ifchannel==null{ 持续 } channel.configureBlockingfalse; channel.registerselector,选择key.OP_READ; //如果key.isReadable{ //System.out.PrintLn未读取; // } ByteBuffer缓冲区=ByteBuffer.allocate1024; 如果channel.readbuffer>0{ buffer.flip; byte[]byteArray=新字节[缓冲区剩余]; buffer.getbyteArray; 字符串表达式=新StringbyteArray,UTF-8; System.out.printlnIOUtil.now+接收请求:+表达式; 字符串结果=null; 响应通道、结果; } }捕捉异常{ e、 打印跟踪; } } } } 公共空间关闭{ IOUtil.closeselector; IOUtil.closeserverChannel; } private void ResponseCheckChannel通道,字符串响应引发IOException{ 回复=你好回复; System.out.printlnIOUtil.now+发送响应:+响应; 字节[]字节=response.getBytes; ByteBuffer缓冲区=ByteBuffer.allocatebytes.length; buffer.putbytes; buffer.flip; channel.writebuffer; } 公共静态无效字符串[]args{ 新的NioServerIOUtil.DEFAULT\u PORT.start; }
} 您描述的异常可能发生的唯一方式是,如果您在非ServerSocketChannel的通道上尝试该类型转换,如果其密钥已准备就绪但“不可接受”,则可能发生这种情况。很明显,当您遇到此异常时,您没有步骤2,因此您处理了一个“可读”通道,就好像它是一个“可接受”通道一样

因此,您发布的代码实际上并没有显示出这个问题,但它确实存在大量其他问题。您不需要仅仅因为某个键无效就关闭选择器,如果该键无效,则该键已被取消,因此您不需要取消该键,而且关闭通道会取消该键。为什么您对接受并注册OP_READ频道的OP_READ/isReadable不感兴趣?为什么你不等待OP_read就试图从你刚刚接受的频道阅读

扔掉它,好好看看JavaNIO教程

我确实想评论一下客户中的一条胡说八道:

socketChannel = SocketChannel.open(); 
socketChannel.configureBlocking(false); 
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
    yield();
}
在这里,您正在以非阻塞模式执行阻塞模式连接的等效操作。它可以全部替换为:

socketChannel = SocketChannel.open(); 
socketChannel.connect(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false); 
然后:


在这里,您正在注册一个已经发生的事件。真奇怪。您将永远无法从已连接的套接字获得OP_CONNECT。移除。

从未见过这种情况。您不需要仅仅因为某个键无效就关闭选择器,如果该键无效,则该键已被取消,因此您不需要取消该键,而且关闭通道会取消该键。这里的代码很奇怪。为什么是ar
你对接受并注册OP_READ频道的OP_READ不感兴趣吗?为什么你不等待OP_read就试图从你刚刚接受的频道阅读?当你准备从频道上阅读时,为什么你不打印而阅读呢?我建议您好好看看NIO教程。此代码是垃圾。您也在错误的键上调用key.isReadable。无法复制。很明显,这不是真正的代码。对不起,我错了。我已经把它藏起来了。
socketChannel.register(selector, SelectionKey.OP_CONNECT);