2017-09-20 13 views
-2

J'utilise un pseudo-code pour décrire le problème d'abord, et je vais coller le code entier dans le suivant, qui peut s'exécuter dans le local.CastException dans le serveur Nio: SocketChannelImpl ne peut pas être converti en ServerSocketChannel

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(); 
    ... 
    } 
    } 

l'exception est survenue dans l'étape 4, puis je fais un chèque à l'étape 3, mais il ne peut pas passer le contrôle et aller dans une boucle morte acceptable. de temps en temps, il peut recevoir et répondre normalement et je n'ai pas fait de changement, c'est trop étrange pour moi. ici je colle le code et espère que quelqu'un peut m'aider. Merci.

package io.Nio; 
 

 
import java.io.IOException; 
 
import java.net.InetSocketAddress; 
 
import java.nio.ByteBuffer; 
 
import java.nio.channels.ClosedSelectorException; 
 
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 java.util.Set; 
 

 
import io.util.IOUtil; 
 

 
public class NioServer extends Thread{ 
 

 
\t private int port; 
 

 
\t private Selector selector; 
 

 
\t private ServerSocketChannel serverChannel; 
 

 
\t public NioServer(int port){ 
 
\t \t this.port = port; 
 
\t } 
 

 
\t @Override 
 
\t public void run() { 
 
\t \t try{ 
 
\t \t \t selector = Selector.open(); 
 
\t \t \t serverChannel = ServerSocketChannel.open(); 
 
\t \t \t serverChannel.configureBlocking(false); 
 
\t \t \t serverChannel.socket().bind(new InetSocketAddress(port), 1024); 
 
\t \t \t serverChannel.register(selector, SelectionKey.OP_ACCEPT); 
 
\t \t }catch(IOException e){ 
 
\t \t \t IOUtil.close(serverChannel); 
 
\t \t } 
 

 
\t \t System.out.println("server start:" + port); 
 
\t \t while(true){ 
 
\t \t \t try { 
 
\t \t \t \t selector.select(); 
 
\t \t \t } catch (ClosedSelectorException e) { 
 
\t \t \t \t e.printStackTrace(); 
 
\t \t \t }catch (IOException e) { 
 
\t \t \t \t e.printStackTrace(); 
 
\t \t \t } 
 
\t \t \t Set<SelectionKey> keys = selector.selectedKeys(); 
 
\t \t \t Iterator<SelectionKey> it = keys.iterator(); 
 
\t \t \t while (it.hasNext()) { 
 
\t \t \t \t SelectionKey key = it.next(); 
 
\t \t \t \t if(!key.isValid()){ 
 
\t \t \t \t \t key.cancel(); 
 
\t \t \t \t \t IOUtil.close(key.channel()); 
 
\t \t \t \t \t IOUtil.close(key.selector()); 
 
\t \t \t \t \t System.out.println(IOUtil.now() + "clear a invalid key."); 
 
\t \t \t \t \t continue; 
 
\t \t \t \t } 
 
\t \t \t \t 
 
\t \t \t \t // i put a check here,if is not Acceptable,then continue, but it's a dead loop 
 
\t \t \t \t if(!key.isAcceptable()){ 
 
\t \t \t \t \t System.out.println("not Acceptable"); 
 
\t \t \t \t \t continue; 
 
\t \t \t \t } 
 

 
\t \t \t \t try { 
 
\t \t \t \t \t //Exception here: SocketChannelImpl cannot be cast to ServerSocketChannel 
 
\t \t \t \t \t ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); 
 
\t \t \t \t \t SocketChannel channel = serverChannel.accept(); 
 
\t \t \t \t \t if(channel == null){ 
 
\t \t \t \t \t \t continue; 
 
\t \t \t \t \t } 
 

 
\t \t \t \t \t channel.configureBlocking(false); 
 
\t \t \t \t \t channel.register(selector, SelectionKey.OP_READ); 
 
\t \t \t \t \t 
 
// \t \t \t \t \t if (key.isReadable()){ 
 
// \t \t \t \t \t \t System.out.println("not read"); 
 
// \t \t \t \t \t } 
 

 
\t \t \t \t \t ByteBuffer buffer = ByteBuffer.allocate(1024); 
 
\t \t \t \t \t if (channel.read(buffer) > 0) { 
 
\t \t \t \t \t \t buffer.flip(); 
 
\t \t \t \t \t \t byte[] byteArray = new byte[buffer.remaining()]; 
 
\t \t \t \t \t \t buffer.get(byteArray); 
 
\t \t \t \t \t \t String expression = new String(byteArray, "UTF-8"); 
 
\t \t \t \t \t \t System.out.println(IOUtil.now() + "receive request:" + expression); 
 
\t \t \t \t \t \t String result = null; 
 
\t \t \t \t \t \t response(channel, result); 
 
\t \t \t \t \t } 
 
\t \t \t \t }catch (IOException e) { 
 
\t \t \t \t \t e.printStackTrace(); 
 
\t \t \t \t } 
 
\t \t \t } 
 
\t \t } 
 

 
\t } 
 

 
\t public void shutdown(){ 
 
\t \t IOUtil.close(selector); 
 
\t \t IOUtil.close(serverChannel); 
 
\t } 
 

 
\t private void response(SocketChannel channel, String response) throws IOException { 
 
\t \t response = "hello response"; 
 
\t \t System.out.println(IOUtil.now() + "send response:"+ response); 
 
\t \t byte[] bytes = response.getBytes(); 
 
\t \t ByteBuffer buffer = ByteBuffer.allocate(bytes.length); 
 
\t \t buffer.put(bytes); 
 
\t \t buffer.flip(); 
 
\t \t channel.write(buffer); 
 
\t } 
 

 
\t public static void main(String[] args) { 
 
\t \t new NioServer(IOUtil.DEFAULT_PORT).start(); 
 
\t } 
 
}

package io.Nio; 
 

 
import java.io.IOException; 
 
import java.net.InetSocketAddress; 
 
import java.nio.ByteBuffer; 
 
import java.nio.channels.SelectionKey; 
 
import java.nio.channels.Selector; 
 
import java.nio.channels.SocketChannel; 
 
import java.util.Iterator; 
 
import java.util.concurrent.CountDownLatch; 
 

 
import io.util.IOUtil; 
 

 
public class NioClient extends Thread{ 
 

 
\t private volatile CountDownLatch connectLatch; 
 

 
\t private String ip; 
 

 
\t private int port; 
 

 
\t private Selector selector; 
 

 
\t private SocketChannel socketChannel; 
 

 

 
\t private NioClient(String ip, int port) { 
 
\t \t this.ip = ip; 
 
\t \t this.port = port; 
 
\t \t connectLatch = new CountDownLatch(1); 
 
\t } 
 

 
\t public static NioClient open(String ip, int port){ 
 
\t \t NioClient client = new NioClient(ip,port); 
 
\t \t client.start(); 
 
\t \t return client; 
 
\t } 
 

 
\t @Override 
 
\t public void run(){ 
 
\t \t try{ 
 
\t \t \t long begin = System.currentTimeMillis(); 
 
\t \t \t System.out.println(IOUtil.now() + "start client"); 
 
\t \t \t selector = Selector.open(); 
 
\t \t \t socketChannel = SocketChannel.open(); 
 
\t \t \t socketChannel.configureBlocking(false); 
 
\t \t \t socketChannel.connect(new InetSocketAddress(ip,port)); 
 
\t \t \t while(!socketChannel.finishConnect()){ 
 
\t \t \t \t yield(); 
 
\t \t \t } 
 
\t \t \t System.out.println(IOUtil.now() + "cost time:" + (System.currentTimeMillis() - begin) + "ms"); 
 
\t \t \t connectLatch.countDown(); 
 
\t \t \t socketChannel.register(selector, SelectionKey.OP_CONNECT); 
 
\t \t \t while(true){ 
 
\t \t \t \t selector.select(); 
 
\t \t \t \t Iterator<SelectionKey> it = selector.selectedKeys().iterator(); 
 
\t \t \t \t while(it.hasNext()){ 
 
\t \t \t \t \t SelectionKey key = it.next(); 
 
\t \t \t \t \t if(!key.isValid() || !key.isReadable()){ 
 
\t \t \t \t \t \t continue; 
 
\t \t \t \t \t } 
 

 
\t \t \t \t \t SocketChannel channel = (SocketChannel) key.channel(); 
 
\t \t \t \t \t ByteBuffer buffer = ByteBuffer.allocate(1024); 
 
\t \t \t \t \t if(channel.read(buffer) > 0){ 
 
\t \t \t \t \t \t buffer.flip(); 
 
\t \t \t \t \t \t byte[] byteArray = new byte[buffer.remaining()]; 
 
\t \t \t \t \t \t buffer.get(byteArray); 
 
\t \t \t \t \t \t String response = new String(byteArray,"UTF-8"); 
 
\t \t \t \t \t \t System.out.println(IOUtil.now() + "receive response:" + response); 
 
\t \t \t \t \t } 
 
\t \t \t \t } 
 
\t \t \t } 
 
\t \t }catch(IOException e){ 
 
\t \t \t e.printStackTrace(); 
 
\t \t } 
 
\t } 
 
\t 
 
\t public void request(String request) { 
 
\t \t try { 
 
\t \t \t connectLatch.await(); 
 
\t \t } catch (InterruptedException e) { 
 
\t \t \t System.out.println(IOUtil.now() + "interrupted" + e.getMessage()); 
 
\t \t } 
 
\t \t try { 
 
\t \t \t byte[] bytes = request.getBytes(); 
 
\t \t \t ByteBuffer buffer = ByteBuffer.allocate(bytes.length); 
 
\t \t \t buffer.put(bytes); 
 
\t \t \t buffer.flip(); 
 
\t \t \t socketChannel.register(selector, SelectionKey.OP_READ); 
 
\t \t \t //TODO 
 
\t \t \t System.out.println(IOUtil.now() + "send request:" + request); 
 
\t \t \t socketChannel.write(buffer); 
 
\t \t } catch (IOException e) { 
 
\t \t \t e.printStackTrace(); 
 
\t \t } 
 
\t } 
 

 
\t public static void main(final String[] args) throws InterruptedException { 
 
\t \t NioClient client = NioClient.open(IOUtil.DEFAULT_HOST, IOUtil.DEFAULT_PORT); 
 
\t \t client.request("hello"); 
 
// \t \t while(true){ 
 
// \t \t \t sleep(500); 
 
// \t \t \t String request = IOUtil.buileRequest(1991); 
 
// \t \t \t client.request(request); 
 
// \t \t } 
 
\t } 
 
}

package io.util; 
 

 
import java.io.Closeable; 
 
import java.io.IOException; 
 
import java.text.SimpleDateFormat; 
 
import java.util.Date; 
 
import java.util.Random; 
 

 
public class IOUtil { 
 

 
\t public static final String DEFAULT_HOST = "127.0.0.1"; 
 

 
\t public static final int DEFAULT_PORT = 8080; 
 

 
\t public static final String operators[] = {"+", "-", "*", "/"}; 
 

 
\t public static final int CLIENNT_NUM = 10; 
 

 
\t public static final boolean CLIENT_CLOSEABLE = true; 
 
\t 
 
\t public static String now(){ 
 
\t \t return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS ").format(new Date()); 
 
\t } 
 
\t 
 
\t public static String buileRequest(int seed){ 
 
\t \t Random random = new Random(seed); 
 
\t \t return random.nextInt(10) + IOUtil.operators[random.nextInt(4)] + (random.nextInt(10) + 1); 
 
\t } 
 

 
\t public static void close(Closeable io) { 
 
\t \t if (io != null) { 
 
\t \t \t try { 
 
\t \t \t \t io.close(); 
 
\t \t \t } catch (IOException e) { 
 
\t \t \t \t e.printStackTrace(); 
 
\t \t \t } 
 
\t \t } 
 
\t } 
 

 
}

+0

Je n'ai jamais vu ça. Vous n'avez pas besoin de fermer le sélecteur juste parce qu'une clé est invalide, et si elle est invalide elle est déjà annulée, donc vous n'avez pas besoin de l'annuler, et de toute façon la fermeture du canal annule la clé. Code très étrange ici. Pourquoi n'êtes-vous pas intéressé par OP_READ sur les chaînes que vous avez acceptées et enregistrées pour OP_READ? Et pourquoi essayez-vous de lire depuis une chaîne que vous venez d'accepter, sans attendre OP_READ? Et pourquoi imprimez-vous "pas lu" lorsque vous êtes sur le point de lire à partir de la chaîne? Je vous suggère de bien regarder le tutoriel NIO. Ce code est un déchet. – EJP

+0

Vous appelez également 'key.isReadable()' sur la mauvaise clé. – EJP

+0

Impossible de reproduire.Clairement, ce n'est pas le vrai code. – EJP

Répondre

0

La seule façon que vous décrivez l'exception c Si vous tentez cette opération sur un canal qui n'est pas un ServerSocketChannel, cela peut se produire si sa clé est prête mais pas "acceptable". Il est clair que vous n'aviez pas votre étape 2 lorsque vous avez reçu cette exception, donc vous avez traité un canal "lisible" comme s'il s'agissait d'un canal "acceptable". Par conséquent, le code que vous avez publié ne présente pas réellement ce problème, mais il en contient un grand nombre. Vous n'avez pas besoin de fermer le sélecteur juste parce qu'une clé est invalide, et si elle est invalide elle est déjà annulée, donc vous n'avez pas besoin de l'annuler, et de toute façon la fermeture du canal annule la clé. Pourquoi n'êtes-vous pas intéressé par OP_READ/isReadable() sur les canaux que vous avez acceptés et enregistrés pour OP_READ? Et pourquoi essayez-vous de lire depuis une chaîne que vous venez d'accepter, sans attendre OP_READ?

Jetez-le et regardez bien le didacticiel Java NIO.

Je veux faire des commentaires sur un morceau particulier de non-sens dans le client:

socketChannel = SocketChannel.open(); 
socketChannel.configureBlocking(false); 
socketChannel.connect(new InetSocketAddress(ip,port)); 
while(!socketChannel.finishConnect()){ 
    yield(); 
} 

Ici vous effectuez l'équivalent d'un mode de blocage se connecter en mode non-bloquant. Il peut tout être remplacé par:

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

Puis:

socketChannel.register(selector, SelectionKey.OP_CONNECT); 

Ici vous vous inscrivez à un événement qui a déjà eu lieu. Vraiment bizarre. Vous n'obtiendrez jamais OP_CONNECT à partir d'une socket déjà connectée. Retirer.