2015-01-31 3 views
1

Я изучаю книгу Pro Java 7 NIO.2, чтобы лучше понять пакет NIO и хотел бы работать над некоторым сетевым кодом, чтобы лучше понять, как работает netty в фоновом режиме. Общая ошибка имеет смысл, но причина, по которой возникает ошибка, выходит за рамки моего понимания.CastException с сокетами Java NIO

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

Первое, что я сделал, чтобы убедиться, что ни один из моего кода не импортируя ничего из упаковки солнца, и что все, что было в самом деле, с помощью пакета 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, и в большинстве случаев строка имеет одно и то же значение (Minus some names).

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) 

Я, вероятно, следует добавить, что реализация типажей SocketChannel образуют selectionkey.channel() приходит прямо из JavaDocs - http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2

+0

Посмотрите на источник sun.nio.ch.ServerSocketChannelImpl, он, кажется, расширяет java.nio.channels.ServerSocketChannel и реализует sun.nio.ch.SelChImpl, но, похоже, нет связи с java.nio.channels .SocketChannel – hammerfest

+0

@hammerfest - Самая странная проблема и самый ошеломляющий из всех заключается в том, что это происходит только при локальном подключении клиента. (Этот клиент находится на том же компьютере, что и сервер) – Hobbyist

ответ

1

Вы ошиблись SelectionKey до new Connection(...). Вы передаете ключ сокета сервера. Ключ, который вы должны передать, - это ключ принятого сокета, который является результатом socketChannel.register(), на следующей строке.

+0

Спасибо, мне было интересно, где я ошибся. Все это прекрасно работает. – Hobbyist

-2

Классы sun.nio.ch. *, как представляется, содержат некоторые реализации интерфейсов в пакетах java.nio. *; кроссовер для другого пакета происходит в классах реализации, которые вы используете. Там нет большой тайны.

Рассматривая источник для sun.nio.ch.ServerSocketChannelImpl, я вижу, что он реализует java.nio.channels.ServerSocketChannel, а не java.nio.channels.SocketChannel. Это реализация канала, а не реализация сокета. И ServerSocketChannel, и SocketChannel (в java.nio.channels) расширяют AbstractSelectableChannel, но они являются братьями и сестрами в иерархии наследования, а не предком/потомком.

Надеюсь, что это поможет.

+0

Проблема заключается в том, что ServerSocketChannel отправляется из «SelectionKey», когда я создаю локальное соединение, однако, если я делаю удаленное соединение (с компьютера по сети в который сервер не запущен), он регистрируется как SocketChannel, как он должен, и продолжает процесс связи между сервером и клиентом. – Hobbyist

+0

Будет ли кастинг в AbstractSelectableChannel делать то, что вы хотите? Я не знаком с классами сокетов java.nio, поэтому я просто снимаю в темноте. Дело в том, что оба класса, о которых вы говорите, могут в конечном итоге расширить его. Боюсь, я не могу объяснить, почему у двух классов есть общие методы (bind, socket, accept), которые не определены в абстрактном классе - мне кажется, это было бы то, что хотелось бы. – arcy

+0

@arcy Нет, это не так. Ему нужно будет читать и писать. И у них нет методов 'accept()'. – EJP

Смежные вопросы