2009-05-25 8 views
1

Salut J'essaie de mettre en œuvre un serveur Java NIO simple; qui enregistre le socketChannel avec le sélecteur. Par conséquent, je souhaite écouter le client et envoyer une réponse en retour. Après que le socketChannel est enregistré avec le sélecteur, même si le client (non NIO) envoie des données, le serveur n'est pas capable de lire; howerver la clé générée est toujours itérée.Le serveur NIO ne peut pas écouter le client

détaillée: Côté serveur:

**First thread**: 

public void run() { while (true) {

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
    serverSocketChannel.configureBlocking(true); 
    serverSocketChannel.socket().bind(inetAdressOfServer); 
    SocketChannel clientChannel = serverSocketChannel.accept(); 
    new Listener("").addSocketChannel(clientChannel); 

}} 

**Second Thread**: 

    static Selector selector = Selector.open(); 
    public boolean addSocketChannel(SocketChannel clientChannel) { 

     SelectionKey key = clientSocketChannel.register(selector, selector.OP_READ|SelectionKey.OP_WRITE);    
     key.attach(new ChannelCallback(clientSocketChannel)); 
     return key.isValid(); 
    } 

    public void run() { 

     Set keysSet = selector.keys(); 
     Iterator i = keysSet.iterator();   
     while (i.hasNext()) { 
      SelectionKey key = (SelectionKey) i.next(); 
     } 

     if (key.isReadable()) { 
      //read and do something 
     } 
    } 



Client Side: 

Socket socket = new Socket(serverIP, serverPort);  
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());  
dos.writeBytes(str + "\n"); 

NB: Lorsque vous avez terminé en fil unique, le même programme fonctionne, mais Lorsqu'elle est mise en œuvre de la manière mentionnée ci-dessus, elle ne doit pas écouter le client. S'il vous plaît aidez-moi à résoudre ce problème.

+0

Je me demande à chaque fois que de nos jours, les gens utilisent encore le style d'écriture Java-1.4 de 6 ans pour les itérateurs. Les anciens livres et tutoriels Java sont-ils encore si répandus? Ça me rend triste. –

Répondre

1

Il est très difficile de voir ce que vous avez fait, mais il semble que ce que vous avez marqué comme "deuxième thread" est utilisé par les deux threads (confusion sur l'implémentation Runnable/extension Thread et threads réels?). En particulier, je devine le new Listener construit et commence un fil. Vous appelez alors addSocketChannel dans le premier fil de discussion. Par conséquent, il existe une condition de concurrence. Il est également une mauvaise idée de faire selector statique.

+0

Salut Tom, désolé le code n'était pas clair. Le premier thread s'exécute dans une classe différente qui a la logique d'accepter le socketchannel et le passe pour être enregistré. S'il vous plaît pouvez-vous me faire savoir quelles modifications devraient être faites. Merci. – Nilesh

+0

Il semblerait que le plus court correctif possible pourrait être de déplacer l'appel à addSocketChannel vers le constructeur de l'écouteur (et de l'appeler avant de démarrer le thread). Cependant, vous devez vraiment trier vos threads. –

+0

Salut Tom, pourriez-vous s'il vous plaît me donner un pointeur pour apprendre et améliorer le filetage NIO. Aussi pourriez-vous me faire savoir s'il y a une alternative au sélecteur statique, ne pourrait pas le faire fonctionner avec rendre volatile.Y a-t-il une meilleure approche que celle-ci? – Nilesh

1

Lecture de travaux lus à partir d'un autre fil, voici les problèmes évidents avec votre code.

public void run() { 
    Set keysSet = selector.keys(); 

Ici vous prenez le jeu de clés de la iterator, mais il n'y a pas de code jamais faire select() ou selectNow() sur le sélecteur, donc ce jeu sera toujours vide. Ceci ne compile même pas, la vérification de 'read' sur la clé doit être faite à l'intérieur du bloc-temps.

SelectionKey key = clientSocketChannel.register(selector, 
               SelectionKey.OP_READ | 
               SelectionKey.OP_WRITE);    

Deux problèmes: Le canal doit être réglé en mode non-bloquant avant cela se fait et SelectionKey.OP_WRITE ne doit pas être réglé à moins que vous voulez que la clé à retourner chaque fois que vous exécutez une sélection.

Vous ne devez définir SelectionKey.OP_WRITE que si vous envisagez d'effectuer une écriture. Enfin, l'utilisation de deux fils est très peu conventionnelle. La méthode recommandée pour cela consiste à enregistrer le ServerSocketChannel dans le sélecteur avec OP_ACCEPT et à exécuter l'acceptation sur le ServerSocket sur le même thread que read/write.

+0

Merci beaucoup Nuoji, je pourrais faire tourner le code. Je suis d'accord avec vous en ce qui concerne le concept de fil n'est pas conventionnel, mais c'est juste mon essai de voir l'optimisation des performances. Pourriez-vous s'il vous plaît me dire comment marquer la key.isReadable à false après avoir lu les données. – Nilesh

+1

Généralement, vous devez supprimer la clé de l'ensemble après la lecture, en utilisant iterator.remove(). J'ai l'impression de me souvenir que sinon, il apparaîtra chaque fois que vous faites un select(). Après remove(), il est garanti que la clé ne fera pas partie des clés sélectionnées tant qu'il n'y aura pas de données à lire à nouveau. Bien sûr, si vous voulez désactiver l'intérêt sur la touche (vous ne voulez pas continuer à lire sur cette connexion), vous devez plutôt utiliser key.interestOps (int ops) pour définir les nouveaux intérêts de cette touche (par exemple 0 pour ni lire ni écrire). – Nuoji

+0

'Selector.keys()' renvoie l'ensemble des clés * enregistrées *, pas * prêtes *, donc si vous avez déjà appelé 'select()' ou non, cela ne fait aucune différence. – EJP

Questions connexes