使用Java NIO套接字的CastException

时间:2015-01-31 11:55:17

标签: java class sockets nio

我一直在研究这本书Pro Java 7 NIO.2,以便更好地了解NIO包,并希望研究一些与网络相关的代码,以便更好地理解netty在后台的工作原理。总体错误是有道理的,但错误被抛出的原因超出了我的理解范围。

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

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

我尝试将客户端连接到服务器时会抛出此错误,但实际上困扰我的一般事实是它尝试输入到ServerSocketChannel而不是只是一个SocketChannel,这让我相信服务器很困惑。

我事先为下面的代码墙道歉,但是因为每个人总是要求我发布一个正在运行的例子,我打算这样做。这是三个小类文件。

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)

我应该补充说,类型化socketconnel的实现形成了selectionkey.channel()直接来自JavaDocs - http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:-2)

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

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

希望有所帮助。