2010-11-02 8 views
4

J'ai un fournisseur JMS minimal, qui envoie des messages sur le sujet des messages UDP et la file d'attente sur TCP. J'utilise un seul sélecteur pour gérer les clés de sélection UDP et TCP (en enregistrant à la fois SocketChannels et DatagramChannels). Mon problème est: si je n'envoie et ne reçois que des paquets UDP, tout se passe bien, mais dès que je commence à écrire sur un socket TCP (en utilisant Selector.wakeup() pour que le sélecteur fasse l'écriture proprement dite), Le sélecteur entre dans une boucle infinie, renvoyant un jeu de clés de sélection vide et mangeant 100% du processeur.Selector.select() démarre une boucle infinie

Le code de la boucle principale (un peu simplifiée) est:

public void run() { 
    while (!isInterrupted()) { 
    try { 
    selector.select(); 
    } catch (final IOException ex) { 
    break; 
    } 

    final Iterator<SelectionKey> selKeys = selector.selectedKeys().iterator(); 
    while (selKeys.hasNext()) { 
    final SelectionKey key = selKeys.next(); 
    selKeys.remove(); 
    if (key.isValid()) { 
    if (key.isReadable()) { 
     this.read(key); 
    } 
    if (key.isConnectable()) { 
     this.connect(key); 
    } 
    if (key.isAcceptable()) { 
     this.accept(key); 
    } 
    if (key.isWritable()) { 
     this.write(key); 
     key.cancel(); 
    } 
    } 
    } 
    synchronized(waitingToWrite) { 
    for (final SelectableChannel channel: waitingToWrite) { 
    try { 
     channel.register(selector, SelectionKey.OP_WRITE); 
    } catch (ClosedChannelException ex) { 
     // TODO: reopen 
    } 
    } 
    waitingToWrite.clear(); 
    } 
    } 
} 

Et pour envoyer UDP (envoi TCP est similaire):

public void udpSend(final String xmlString) throws IOException { 
    synchronized(outbox) { 
    outbox.add(xmlString); 
    } 
    synchronized(waitingToWrite) { 
    waitingToWrite.add(dataOutChannel); 
    } 
    selector.wakeup(); 
} 

Alors, quel est le problème ici? Dois-je utiliser 2 sélecteurs différents pour gérer les paquets UDP et TCP?

+0

J'ai trouvé quelqu'un qui a un problème similaire: http://web.archiveorange.com/archive/v/xyOAds3Uh2NVesezD5VH. Mais pas encore de solution ... –

Répondre

1

Problème disparu après la mise à niveau vers Java 1.6.0_22.

0

Modifier votre conception d'avoir un fil par connexion réseau entrant.

Le sélecteur doit être utilisé lorsque vous utilisez un thread pour traiter les messages entrants sur plusieurs sockets TCP. Vous enregistrez chaque socket avec le sélecteur, puis select(), ce qui bloque jusqu'à ce qu'il y ait des données disponibles sur l'un d'entre eux. Ensuite, vous parcourez chaque clé et traitez les données en attente. C'est la méthode que j'ai toujours utilisée pour écrire du code C, et ça marchera, mais je ne pense pas que ce soit la meilleure façon de le faire en Java.

Java a un grand support natif des threads, ce qui ne fonctionne pas C. Je pense qu'il est beaucoup plus logique d'avoir un thread par socket TCP et de ne pas utiliser de sélecteurs du tout. Si vous effectuez une opération de lecture sur un socket, le thread se bloque jusqu'à ce que les données arrivent ou que le socket soit fermé. C'est effectivement la même chose que de sélectionner avec un seul canal enregistré.

Si vous voulez que cela fonctionne avec un seul thread, vous devez utiliser le sélecteur uniquement pour les sockets TCP où vous voulez des connexions entrantes. De cette façon, la seule fois où l'appel à select() reviendra est quand il y a des données entrantes en attente sur l'une des sockets. Ce fil sera endormi à tous les autres moments, et aucune autre opération ne le réveillera.

+0

Je ne vois pas pourquoi il ne devrait pas essayer de travailler sur un paradigme mono-thread. C'est certainement une performance valable. – krico

+0

Je voudrais avoir tous ensemble dans un seul fil, puisque cette classe est à la fois un client et un serveur. –

+0

Je me trouve dans une situation similaire, et je choisis un modèle de conception similaire. J'utilise maintenant un thread pour accepter les connexions entrantes et lire des connexions ouvertes. Je supprimerais presque cette réponse, mais quelqu'un pourrait trouver ce point de vue utile. –

1

Je vous suggère de vérifier la valeur de retour de la méthode select().

try { 
if(selector.select() == 0) continue; 
} catch (final IOException ex) { 
break; 
} 

Avez-vous essayé le débogage pour voir où se trouve la boucle?

Edit:

  • Je recommande que, au lieu d'appeler "supprimer()" sur le iterator, vous appelez selectedKeys.clear() après avoir itérer sur eux. Il est possible que l'implémentation de l'itérateur ne le supprime pas de l'ensemble sous-jacent.
  • Vérifiez que vous n'enregistrez pas OP_CONNECT sur un canal connecté.
+0

Déjà essayé de faire cela, la boucle est sur l'appel select(), et la valeur de retour est toujours 0. –

+0

lorsque vous faites un selector.wakeup(), select() reviendra avec 0 la prochaine fois que vous l'appelez. – krico

+0

J'ai supprimé tous les appels wakeup() de mon code, ne fonctionne toujours pas. Cependant, un seul réveil ne devrait pas déclencher une boucle sans fin. –

1

vous obtenez probablement un IOException et en ignorant dans le bloc catch vide. Jamais faites cela.Et continuer juste après une IOException n'est pratiquement jamais l'action correcte. La seule exception à cette règle que je peux imaginer est une exception SocketTimeoutException, et vous êtes en mode non-bloquant donc vous ne les obtiendrez pas, et vous ne les obtiendrez pas sur les sélecteurs de toute façon. Je voudrais voir le contenu de vos méthodes qui gèrent se connecter, accepter, lire et écrire.

+0

Le bloc catch n'est pas vide, il doit sortir de la boucle while sur les exceptions d'E/S. Toutefois, le problème s'est résolu en passant à la version 1.6 JDK la plus récente. –

+0

Pourquoi le downvote? Une erreur dans le ci-dessus? Si c'est le cas, c'est une courtoisie pour nous tous de le noter. – EJP

+0

Downvote parce que vous n'avez même pas lu le code avant de répondre (catch block non vide) et la réponse est "vous ne recevrez aucune exception de toute façon". Je ne peux pas vraiment comprendre votre point. –