带有 Java 个 NIO 套接字的 CastException

CastException with Java NIO sockets

我一直在学习这本书 Pro Java 7 NIO.2 以更好地了解 NIO 包,并且想编写一些与网络相关的代码以更好地了解 netty 在后台的工作方式。整体错误是有道理的,但为什么会抛出错误是我无法理解的。

java.lang.ClassCastException: sun.nio.ch.ServerSocketChannelImpl cannot be cast to java.nio.channels.SocketChannel

我做的第一件事是确保我的代码 none 正在从 sun 包装中导入任何东西,并且实际上一切都使用 java.nio 包。一切似乎都结账了。

我尝试将客户端连接到服务器时抛出此错误,但真正困扰我的是一般事实,即它正在尝试类型转换为 ServerSocketChannel 并且不仅仅是一个 SocketChannel,这让我相信服务器很困惑。

对于下面的代码墙,我深表歉意,但由于每个人总是要求我 post 一个 运行 示例,所以我打算这样做。这是三个 class 小文件。

TcpProcessor.java

package net.ogserver.proto.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import net.ogserver.proto.connections.Connection;


public class TcpProcessor implements Runnable {

    public static int tcpPort;

    public void run() {
        try (Selector selector = Selector.open();
                ServerSocketChannel serverSocket = ServerSocketChannel.open()) { 
            if((serverSocket.isOpen()) && (selector.isOpen())) {
                serverSocket.configureBlocking(false);
                serverSocket.bind(new InetSocketAddress(tcpPort));
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
                System.out.println("Server has started and is waiting for connections...");
                while(!Thread.interrupted()) {
                    selector.select();
                    Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                    while(keys.hasNext()) {
                        SelectionKey key = (SelectionKey) keys.next();
                        keys.remove();
                        if(!key.isValid()) {
                            continue;
                        }

                        if(key.isAcceptable()) {
                            processIncomingConnection(key, selector);
                        } else if(key.isReadable()) {
                            //processIncomingData(key);
                        } else if(key.isWritable()) {
                            //pushOutgoingData(key);
                        }
                    }
                }
            } else {
                System.err.println("There was an issue constructing the socket.");
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    private void processIncomingConnection(SelectionKey selectionKey, Selector selector) throws IOException {
        ServerSocketChannel serverSocket = (ServerSocketChannel)selectionKey.channel();
        SocketChannel       clientSocket = serverSocket.accept();
        clientSocket.configureBlocking(false);

        System.out.println("Incoming connection from " + clientSocket.getRemoteAddress());

        selectionKey.attach(new Connection(selectionKey));

        clientSocket.register(selector, SelectionKey.OP_READ);
    }
}

Connection.java

package net.ogserver.proto.connections;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

public class Connection {

    private SelectionKey    selectionKey;
    private SocketChannel   clientSocket;

    private ByteBuffer      networkInputBuffer;
    private ByteBuffer      networkOutputBuffer;

    public Connection(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
        this.clientSocket = (SocketChannel)selectionKey.channel();
        this.networkInputBuffer     = ByteBuffer.allocate(1024);
        this.networkOutputBuffer    = ByteBuffer.allocate(8192);
    }

    public SelectionKey getSelectionKey() {
        return selectionKey;
    }

    public ByteBuffer getInputBuffer() {
        return networkInputBuffer;
    }

    public ByteBuffer getOutputBuffer() {
        return networkOutputBuffer;
    }

    public SocketChannel getChannel() {
        return clientSocket;
    }

}

Server.java

package net.ogserver.proto;

import net.ogserver.proto.tcp.TcpProcessor;


public class Server {

    private Thread tcpProcessor;

    public Server(int port) {
        TcpProcessor.tcpPort = port;
        tcpProcessor = new Thread(new TcpProcessor());
        tcpProcessor.start();
    }

    public static void main(String[] args) {
        new Server(5055);
    }
}

调用TcpProcessor#processIncomingConnection时发生错误,调用创建新的Connection实例。抛出此错误的行直接引用了书中的内容,我查看了其他一些 NIO 服务器,其中大多数的行完全相同(减去一些命名)。

this.clientSocket = (SocketChannel)selectionKey.channel();

任何帮助将不胜感激,完整的控制台输出供需要的人使用:

Server has started and is waiting for connections...
Incoming connection from /127.0.0.1:53221
Exception in thread "Thread-0" java.lang.ClassCastException: sun.nio.ch.ServerSocketChannelImpl cannot be cast to java.nio.channels.SocketChannel
    at net.ogserver.proto.connections.Connection.<init>(Connection.java:17)
    at net.ogserver.proto.tcp.TcpProcessor.processIncomingConnection(TcpProcessor.java:60)
    at net.ogserver.proto.tcp.TcpProcessor.run(TcpProcessor.java:37)
    at java.lang.Thread.run(Thread.java:745)

我可能应该补充一点,selectionkey.channel() 中的类型转换 socketchannel 的实现直接来自 JavaDocs -- http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2

sun.nio.ch.* 类 似乎包含 java.nio.* 包中的一些接口实现;到不同包的交叉发生在您正在使用的实现 类 中。没什么大不了的。

通过查看 sun.nio.ch.ServerSocketChannelImpl 的源代码,我发现它实现了 java.nio.channels.ServerSocketChannel,而不是 java.nio.channels.SocketChannel。它是通道实现,而不是套接字实现。 ServerSocketChannel 和 SocketChannel(在 java.nio.channels 中)都扩展了 AbstractSelectableChannel,但它们在继承层次结构中是兄弟,而不是 ancestor/descendant.

希望对您有所帮助。

您将错误的 SelectionKey 传递给了 new Connection(...)。您正在传递服务器套接字的密钥。您应该传递的密钥是接受的套接字密钥,它是下一行 socketChannel.register() 的结果。