简体   繁体   中英

Problem with Client-Server Application with Socket

i hope you guys are doing fine (i'm french btw, excuse my english). I have a problem with my project. This involves developing a client-server program. The client part will read from a data file a set of commands that the client will have to make the server execute. I created the ApplicationClient, ApplicationServer, and Commande class but my ApplicationClient isnt running and throws multiples errors while my ApplicationServer has an infinite running loop (running but without end)

I tried to run my ApplicationClient with those 4 arguments for the socket (hostname, port, the command file and the outputfile)

Here is my code :

Application Client class

 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;
    
    } 
}

When i run ApplicationClient, i get this:

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)

The client can't connect to the client... And ApplicationServer is take infinite time to run...and that's not normal...

Those are the arguments i tried to give to run the ApplicationClient :

127.0.0.1 8080 commandes.txt sortie.txt

I hope someone will be able to help me.

Thank you all, and have a good night.

This is an interesting problem!

Take a look at ObjectInputStream(InputStream in) constructor's javadoc :

Creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header.

In your client ( ApplicationClient.java ) code you have:

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

In your server ( ApplicationServeur.java ) code you have:

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

and here is what happens:

  1. your client tries to open an ObjectInputStream - (1) - and blocks until your server opens its ObjectOutputStream - (4)
  2. your server tries to open an ObjectInputStream - (3) - and blocks until your client opens its ObjectOutputStream - (2)
  3. your client and server are caught waiting on one another indefinitely!

The solution is to change the order of lines (3) and (4) in your ApplicationServeur.java

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM