带有套接字的客户端服务器应用程序出现问题

时间:2020-10-06 22:53:56

标签: java sockets

我希望你们一切都好(我是法国人,请问我的英语)。我的项目有问题。 这涉及开发客户端服务器程序。客户端将从数据文件中读取一组客户端必须执行的命令。 我创建了ApplicationClient,ApplicationServer和Commande类,但是当我的ApplicationServer有无限的运行循环(正在运行但没有结束)时,我的ApplicationClient没有运行并抛出多个错误

我尝试使用套接字的那四个参数(主机名,端口,命令文件和输出文件)运行ApplicationClient

这是我的代码:

应用程序客户端类

 public class ApplicationClient {

private BufferedReader commandesReader;
private FileWriter printWriter;
private PrintWriter sortieWriter;
private InetAddress hostname;
private int port, s_port;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null; 
private String s_host;
private InetAddress serverHostname;
private int portNumber;


/**
* prend le fichier contenant la liste des commandes, et le charge dans une
* variable du type Commande qui est retournée
* 
*/
public Commande saisisCommande(BufferedReader fichier) {
    
  try {
      
  String strCurrentLine = "";
  
  if ((strCurrentLine = fichier.readLine()) != null) {
      
    System.out.println(strCurrentLine);
    return new Commande(strCurrentLine);
    
        }
    } catch (IOException ioe) {
  ioe.printStackTrace();
    }
  return null;
}

/**
* initialise : ouvre les différents fichiers de lecture et écriture
*/
public void initialise(String fichCommandes, String fichSortie) throws Exception {
    
    //ouverture fichier de lecture
    FileReader fichLecture = new FileReader(fichCommandes);
    commandesReader = new BufferedReader(fichLecture);

    //ouverture fichier de sortie
    FileWriter fichEcriture = new FileWriter(fichSortie);
    BufferedWriter buff_writer = new BufferedWriter(fichEcriture);
    sortieWriter = new PrintWriter(buff_writer);
           

}
/**
* prend une Commande dûment formatée, et la fait exécuter par le serveur. Le résultat de
* l’exécution est retournée. Si la commande ne retourne pas de résultat, on retourne null.
* Chaque appel doit ouvrir une connexion, exécuter, et fermer la connexion. Si vous le
* souhaitez, vous pourriez écrire six fonctions spécialisées, une par type de commande
* décrit plus haut, qui seront appelées par traiteCommande(Commande uneCommande)
*/
public Object traiteCommande(Commande uneCommande) throws IOException, ClassNotFoundException {
    
    //Création du socket pour permettre la connexion au serveur
    Socket clientSocket = new Socket(serverHostname, portNumber);
          
     //Création des flux d'entrée et de sortie en direction du serveur
     
    //Entrée
     ois = new ObjectInputStream(clientSocket.getInputStream());
     //Sortie
     oos = new ObjectOutputStream(clientSocket.getOutputStream());
        
     //Envoi de l'objet uneCommande au serveur
     oos.writeObject(uneCommande);
     
     //Résultat renvoyé par le serveur
     Object resultat = ois.readObject();
     
     //Fermeture du serveur, exception en cas de problème
     Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
    
        try {
            clientSocket.close();
            System.out.println("Le serveur est fermé");
            } catch (IOException e) { //Gestion des exceptions
            }
        }});
        
    return resultat;
    
}

/**
* cette méthode vous sera fournie plus tard. Elle indiquera la séquence d’étapes à exécuter
* pour le test. Elle fera des appels successifs à saisisCommande(BufferedReader fichier) et
* traiteCommande(Commande uneCommande).
*/
public void scenario() throws IOException, ClassNotFoundException {
    sortieWriter.println("Debut des traitements:");
    Commande prochaine = saisisCommande(commandesReader);
    while (prochaine != null) {
        sortieWriter.println("\tTraitement de la commande " + prochaine + " ...");
        Object resultat = traiteCommande(prochaine);
        sortieWriter.println("\t\tResultat: " + resultat);
        prochaine = saisisCommande(commandesReader);
    }
sortieWriter.println("Fin des traitements");
}
/**
* programme principal. Prend 4 arguments: 1) “hostname” du serveur, 2) numéro de port,
* 3) nom fichier commandes, et 4) nom fichier sortie. Cette méthode doit créer une
* instance de la classe ApplicationClient, l’initialiser, puis exécuter le scénario
*/
public static void main(String[] args) throws Exception {
    
    if(args.length != 4) {
        System.out.println("Vous devez spécifier 4 arguments !");
        
    } else {
        ApplicationClient applicationClient = new ApplicationClient();
        applicationClient.serverHostname = InetAddress.getByName(args[0]);
        applicationClient.portNumber = new Integer(args[1]);
        applicationClient.initialise(args[2], args[3]);
        applicationClient.scenario();
        applicationClient.sortieWriter.close();
    }
    
}
}

ApplicationServer

public class ApplicationServeur {
    
    private ServerSocket sockServer;
    private ObjectInputStream ois = null;
    private ObjectOutputStream oos = null;
    private String m_rep_source;
    private String m_rep_classes;
    Class chargement_classe = null;
    HashMap<String, Object> liste_objets = new HashMap<String, Object>();
    ArrayList<Class<?>> liste_classes = new ArrayList<Class<?>>();
/**
* prend le numéro de port, crée un SocketServer sur le port
*/
    public ApplicationServeur (int port, String rep_source, String rep_classes) throws IOException {
        
        //Définition du répertoire source et du répertoire où les classes sont présentes
        m_rep_source = rep_source;
    m_rep_classes = rep_classes;
        //Socket
        sockServer = new ServerSocket(port);
    
    }
    /**
    * Se met en attente de connexions des clients. Suite aux connexions, elle lit
    * ce qui est envoyé à travers la Socket, recrée l’objet Commande envoyé par
    * le client, et appellera traiterCommande(Commande uneCommande)
    */
    public void aVosOrdres() throws IOException {

        while(true) {
            Object messageRetour = null;
            //Ouvrir le socket ainsi que les flux d'entrée et de sortie
            Socket socketConnexion = sockServer.accept();
            //Entrée
            ois = new ObjectInputStream(socketConnexion.getInputStream());
            //Sortie
            oos = new ObjectOutputStream(socketConnexion.getOutputStream());
            
            try {
                //Leture de l'envoi à travers le socket
                Commande command = (Commande) ois.readObject();
                traiteCommande(command);
                
                //Informer le client en renvoyant une requête qui servira de réponse
                oos.writeObject(messageRetour);
                
                if(messageRetour == null) {
                    System.out.println("Le serveur indique qu'il n'y a rien à renvoyer pour cette commande");
                }
                
                messageRetour = null;
                
            } catch(Exception e) {
                //  Gestion des exceptions en cas de problème
                System.out.println("Une erreur est survenue, venez réessayer");
            }

        }
    }
    /**
    * prend uneCommande dument formattée, et la traite. Dépendant du type de commande,
    * elle appelle la méthode spécialisée
    */
    public void traiteCommande(Commande uneCommande) throws ClassNotFoundException {
        
        /* On distingue les différents arguments à l'aide des # */
        
        String[] arguments_commande = uneCommande.getCommandeDesc().split("#");
        
        /*Création d'un switch spécifiant les différentes étapes pour le traitement
        de la commande...Dans l'ordre, la commande doit procéder comme cela :
        Compilation - Chargement - Création - Lecture - Écriture - Appel.*/
        
        switch(uneCommande.getCommandeDesc()){
              
            case "Compilation":
                //On utilise la commande traiterCompilation pour compiler
                traiterCompilation(arguments_commande[1]);
                break;
            case "Chargement":
                //On charge la classe via la méthode traiterChargement
                traiterChargement(arguments_commande[1]);
                break;
            case "Création":
                //On crée l'instance d'une classe
                Class anyType_class = String.class;
                anyType_class = Class.forName(arguments_commande[1]);
                traiterCreation(anyType_class,arguments_commande[2]);
                
                if (anyType_class == null) {
                    System.out.println("L'instance de la classe a échouée... veuillez réessayer");
                } else {
                    System.out.println("L'instance de la classe a été créé avec succès");
                }
                break;
            case "Lecture":
                //On procède à la lecture
                Object objLecture;
                objLecture = liste_objets.get(arguments_commande[1]);
                traiterLecture(objLecture, arguments_commande[2]);               
                break;
            case "Écriture":
                //On procède à l'écriture
                Object objEcriture;
                objEcriture = liste_objets.get(arguments_commande[1]);
                traiterEcriture(objEcriture, arguments_commande[2], arguments_commande[3]);   
                break;
            case "Appel":
            //On procède à l'appel en utilisant traiteAppel()
                Object pointeurObjet = liste_objets.get(arguments_commande[1]);
        String[] types_values = null;
        Object[] values = null;
                
        if(arguments_commande.length>3){
                    String[] args = arguments_commande[3].split(",");
                    types_values = new String[args.length];
                    values = new Object[args.length];
                    for(int i=0; i < args.length; i++){
                        String[] keyValue = args[i].split(":");
                        
                        types_values[i] = keyValue[0];
                    for (Class<?> current : liste_classes) {
                        if (current.getName().equals(types_values[i])) {
                        
                        if(keyValue[1].contains("ID")){
                            values[i] = liste_objets.get(keyValue[1].substring(keyValue[1].indexOf("(")+1, keyValue[1].indexOf(")")));
                        }
                
        }
        }
            }
            }
                
            traiterAppel(pointeurObjet, arguments_commande[2], types_values, values);
                
            break;
                
        }
    }
    /**
    * traiterLecture : traite la lecture d’un attribut. Renvoies le résultat par le
    * socket
    */
    public void traiterLecture(Object pointeurObjet, String attribut) {
        
        try {
            Object obj;
            //Création de la méthode pour l'écriture de l'attribut
            Method read_method = pointeurObjet.getClass().getMethod("get" + attribut.substring(0, 1).toUpperCase() + attribut.substring(1));
            
            /*La suite de la procédure consiste à appeler la méthode pour l'écriture de l'attribut
            L'objet obj renverra le résultat de la méthode*/
            
            obj = read_method.invoke(pointeurObjet);
            
            /*On vérifie si la méthode a correctement été appelé ou si elle ne renvoie rien, dans ce cas
            on l'informe via un message */
            
            if(obj == null) {
                System.out.println("La méthode de l'attribut : " + attribut + " a procédée avec succès, la méthode renvoie :" + obj.toString());
            } else {
                System.out.println("La méthode de l'attribut : " + attribut + " a échouée" );
            }            
            
        } catch (Exception e) {
            System.out.println("Une erreur est survenue, veuillez réessayer" + e.getMessage());
        }
        
    
    }
    /**
    * traiterEcriture : traite l’écriture d’un attribut. Confirmes au client que l’écriture
    * s’est faite correctement.
    */
    public void traiterEcriture(Object pointeurObjet, String attribut, Object valeur) {
        
        try {
            Object obj;
            //Création de la méthode pour l'écriture de l'attribut
            Method write_method = pointeurObjet.getClass().getMethod("set" + attribut.substring(0, 1).toUpperCase() + attribut.substring(1), valeur.getClass());
            
            /*La suite de la procédure consiste à appeler la méthode pour l'écriture de l'attribut
            L'objet obj renverra le résultat de la méthode*/
            
            obj = write_method.invoke(pointeurObjet, valeur);
            
            /*On vérifie si la méthode a correctement été appelé ou si elle ne renvoie rien, dans ce cas
            on l'informe via un message */
            
            if(obj == null) {
                System.out.println("La méthode de l'attribut : " + attribut + " a procédée avec succès, la méthode renvoie :" + obj.toString() );
            } else {
                System.out.println("La méthode de l'attribut : " + attribut + " a échouée" );
            }
          
            
            
        } catch (Exception e) {
            System.out.println("Une erreur est survenue, veuillez réessayer" + e.getMessage());
        }
    
    }
    /**
    5
    * traiterCreation : traite la création d’un objet. Confirme au client que la création
    * s’est faite correctement.
    */
    public void traiterCreation(Class classeDeLobjet, String identificateur) {
        
        try {
             Object obj = classeDeLobjet.newInstance();
             if(obj != null) {
                 System.out.println("L'objet : " + obj.toString() + " a été créé avec succès");
                 liste_objets.put(identificateur, obj);
                 
             } else {
                 System.out.println("La création de : " + obj.toString() + " a échouée");
             }
             
        } catch(Exception e){
            System.out.println("Une erreur est survenue, veuillez réessayer" + e.getMessage());
        }
    
    }
    /**
    * traiterChargement : traite le chargement d’une classe. Confirmes au client que la création
    * s’est faite correctement.
    */
    public void traiterChargement(String nomQualifie) {
        
        try {
            //On identifie la classe via son nom, et on utilise un loader pour la charger
            chargement_classe = Class.forName(nomQualifie);
            ClassLoader classLoader = chargement_classe.getClassLoader();
            
            //Si la classLoader renvoie null, on utilise un boostrap class load pour charger la classe
            if(classLoader == null) {
                Class chargement_classe2 = Class.forName("java.lang.String", true, classLoader);
                System.out.println("Classe" + chargement_classe2.getName() + "a été chargé correctement");
                liste_classes.add(chargement_classe2);
            //Sinon, renvoyer le nom de la classe initialement chargée
            } else {
                System.out.println("Classe" + chargement_classe.getName() + "a été chargé correctement");
            //On ajoute la classe à l'array list regroupant toutes les classes
                liste_classes.add(chargement_classe);
            }
  
        } catch(Exception e) {
            System.out.println("Une erreur est survenue, veuillez réessayer" + e.getMessage());
        }
    }
    /**
    * traiterCompilation : traite la compilation d’un fichier source java. Confirme au client
    * que la compilation s’est faite correctement. Le fichier source est donné par son chemin
    * relatif par rapport au chemin des fichiers sources.
     * @param cheminRelatifFichierSource
    */
    public void traiterCompilation(String cheminRelatifFichierSource) {
        
        try{
            /* Création d'un string permettant de distinguer le chemin des différents
            fichiers avec un espace pour la compilation */  
            String[] cheminTousLesFichiers = cheminRelatifFichierSource.split(",");
            
            // On parcourt le fichier à la recherche de tous les fichiers compilables
            for (String cheminTousLesFichier : cheminTousLesFichiers) {
                //Commande de compilation javac
                String compilation_java = "javac" + m_rep_source + "," + cheminTousLesFichier;
            //On renvoit une réponse au client pour confirmer la compilation
                Runtime.getRuntime().exec(compilation_java).waitFor();
            }
        } catch (IOException | InterruptedException e){
           System.out.println("Une erreur est survenue, veuillez réessayer : " + e.getMessage());
        }       
        
    }   
        
   
    /**
    * traiterAppel : traite l’appel d’une méthode, en prenant comme argument l’objet
    * sur lequel on effectue l’appel, le nom de la fonction à appeler, un tableau de nom de
    * types des arguments, et un tableau d’arguments pour la fonction. Le résultat de la
    * fonction est renvoyé par le serveur au client (ou le message que tout s’est bien
    * passé) */
    
    public void traiterAppel(Object pointeurObjet, String nomFonction, String[] types,
    Object[] valeurs) {
     
    try {     
    
        ArrayList<Class> liste_type = new ArrayList<>();
        
        //On crée une liste d'array pour stocker nos valeurs
        
        if (types.length != 0) {
            for(int i = 0; i <types.length; i++) {
                //Création d'un string pour chercher le type dans la liste
                String type_recherche = types[i];              
                liste_type.add(liste_classes.stream().filter(p->p.getName().equals(type_recherche)).findFirst().get());
                
            }
        }
        
        //Création et appel de la méthode par la suite
        Method call_method = pointeurObjet.getClass().getMethod(nomFonction,liste_type.toArray(new Class[0]));
        call_method.invoke(pointeurObjet, valeurs);
        System.out.println("La méthode : " + nomFonction + "a procédée avec succès");
        
    } catch (Exception e) {
        System.out.println("Une erreur est survenue, veuillez réessayer" + e.getMessage());
    } 
    }
    /**
    * programme principal. Prend 4 arguments: 1) numéro de port, 2) répertoire source, 3)
    * répertoire classes, et 4) nom du fichier de traces (sortie)
    * Cette méthode doit créer une instance de la classe ApplicationServeur, l’initialiser
    * puis appeler aVosOrdres sur cet objet
    */
    public static void main(String[] args) throws Exception {
        
        if(args.length != 4) {
            System.out.println("Vous devez spécifier 4 arguments !");
            
        } else {
            int port = Integer.parseInt(args[0]);
            String rep_source = args[1];
            String rep_classes = args[2];
            String fichSortie = args[3];          
            
            ApplicationServeur appliServeur = new ApplicationServeur(port,rep_source, rep_classes);
            
            appliServeur.aVosOrdres();
        }
    
    }   
}

Commande:

import java.io.Serializable;

/**
 *
 * @author Mehdi Zaaboub Ammar
 * Cette classe implémente l’interface Serializable.
 * Elle est utilisée pour emmagasiner la description d’une commande, selon le format spécifié ci-haut.
 * Ce sont des instances de cette classe qui seront sérialisés et qui seront envoyés à travers les sockets ou RMI.
 */
public class Commande implements Serializable {

    private static final long serialVersionUID = 5164651882436L;

    //Correspond à la description de la commande
    
    public String commande_desc;
    
    //Correspond à ligne qui affichera la commande et sa description
    
    public String commande_line = "";
    

    //Attribuer à une description à une commande spécifique
    
    public Commande(String commande) {
    
        commande_desc = commande;
    
    }

    //Récupérer la description de la commande qui la caractérise
    public String getCommandeDesc() {
    
        return commande_desc;
    
    } 

    //Créer le modèle de la ligne qui affichera la commande et sa description
    public String lineString(){
        commande_line = "La commande est : " + commande_desc ;
        return commande_line;
    
    } 
}

当我运行ApplicationClient时,我得到了:

ant -f C:\\Users\\mehdi\\OneDrive\\Documents\\NetBeansProjects\\TP1_Mehdi_8INF957 "-Dapplication.args=127.0.0.1 8080 commandes.txt sortie.txt" run
init:
deps-jar:
Updating property file: C:\Users\mehdi\OneDrive\Documents\NetBeansProjects\TP1_Mehdi_8INF957\build\built-jar.properties
Compiling 1 source file to C:\Users\mehdi\OneDrive\Documents\NetBeansProjects\TP1_Mehdi_8INF957\build\classes
compile:
run:
creation#ca.uqam.registraire.Cours#inf3123
Exception in thread "main" java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:606)
    at java.net.Socket.connect(Socket.java:555)
    at java.net.Socket.<init>(Socket.java:451)
    at java.net.Socket.<init>(Socket.java:261)
    at ca.uqac.inf957.tp1.ApplicationClient.traiteCommande(ApplicationClient.java:88)
    at ca.uqac.inf957.tp1.ApplicationClient.scenario(ApplicationClient.java:127)
    at ca.uqac.inf957.tp1.ApplicationClient.main(ApplicationClient.java:148)
C:\Users\mehdi\OneDrive\Documents\NetBeansProjects\TP1_Mehdi_8INF957\nbproject\build-impl.xml:1040: The following error occurred while executing this line:
C:\Users\mehdi\OneDrive\Documents\NetBeansProjects\TP1_Mehdi_8INF957\nbproject\build-impl.xml:805: Java returned: 1
BUILD FAILED (total time: 2 seconds)

客户端无法连接到客户端... 而且ApplicationServer需要花费无限的时间来运行...这是不正常的...

这些是我为运行ApplicationClient而尝试给出的参数:

127.0.0.1 8080 commandes.txt sortie.txt

我希望有人能够帮助我。

谢谢大家,晚上好。

1 个答案:

答案 0 :(得分:0)

这是一个有趣的问题!

看看ObjectInputStream(InputStream in)构造函数的javadoc

创建一个ObjectInputStream,它从指定的InputStream读取。从流中读取并验证序列化流头。该构造函数将阻塞,直到相应的ObjectOutputStream写入并刷新了头为止。

在您的客户端ApplicationClient.java)代码中,您具有:

// Entrée
ois = new ObjectInputStream(clientSocket.getInputStream());       // (1)
// Sortie
oos = new ObjectOutputStream(clientSocket.getOutputStream());     // (2)

在您的服务器ApplicationServeur.java)代码中,您具有:

// Entrée
ois = new ObjectInputStream(socketConnexion.getInputStream());    // (3)
// Sortie
oos = new ObjectOutputStream(socketConnexion.getOutputStream());  // (4)

这是发生了什么

  1. 您的 client 尝试打开ObjectInputStream-(1),然后阻止,直到服务器打开其ObjectOutputStream-(4)< / li>
  2. 您的服务器尝试打开ObjectInputStream-(3),然后阻止,直到您的客户端打开其ObjectOutputStream-(2)< / li>
  3. 您的客户端和服务器被无限期地等待着等待!

解决方案是更改ApplicationServeur.java中的行(3)和(4)的顺序