IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

avec Java Discussion :

Dilemme du server multi-thread


Sujet :

avec Java

  1. #1
    Membre éclairé Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    311
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 311
    Par défaut Dilemme du server multi-thread
    Bonjour à tous,

    ma question est : comment organiser le code de mon serveur ?

    Voilà comment j'ai fait les choses :
    1. Serveur.jar
      1. Classe qui hérite de Thread pour se connecter au serveur
      2. Classe qui hérite de Thread pour écouter le client (x nombres de clients)

    2. Client.jar
      1. Classe pour envoyer au serveur
      2. Classe qui hérite de Thread pour écouter le serveur


    Dans les classes qui écoutent, j'ai un while qui tourne en boucle sur ObjectInputStream.
    Et quand j'utilise ObjectOutputStream, je l'utilise en même temps que ObjectInputStream est utilisé.
    Et d'après ce que j'ai compris, ça risque de mettre un bazar dans le header si les deux écrivent et lisent en même temps dedans.

    Comment je fais pour envoyer si j'attends de recevoir?
    Merci de votre aide.

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 577
    Par défaut
    Pas très clair tout ça. Du code serait préférable, mais prévoir de le garder court quand même.

    Citation Envoyé par Pecose Voir le message
    Dans les classes qui écoutent, j'ai un while qui tourne en boucle sur ObjectInputStream.
    Et quand j'utilise ObjectOutputStream, je l'utilise en même temps que ObjectInputStream est utilisé.
    Et d'après ce que j'ai compris, ça risque de mettre un bazar dans le header si les deux écrivent et lisent en même temps dedans.
    Avec les sockets, input et output sont complètement indépendants (sauf qu'en fermer un ferme la socket, donc l'autre aussi.) On peut sans problème utiliser les deux en même temps.
    Par contre il faudrait pas que deux threads utilisent l'input en même temps, ni l'output en même temps.

    Mais input et output en même temps, aucun problème. Ce serait différent par exemple en HTTP. Mais avec des sockets, pas de problème.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre éclairé Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    311
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 311
    Par défaut
    D'accord, ce qui veux dire que mon problème viens d’ailleurs.
    Voila comment ça se passe:

    -Je créé un serveur qui maintenant implémente Runnable:
    -le constructeur de Server:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public Server(int port){ 
    	new ServerLauncher(port); 
    }
    -Le constructeur de ServerLauncher:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public ServerLauncher(int port){ 
    	this.port = port; 
    	this.start();
    }
    -Le run de ServerLauncher:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void run(){
    	try {
    		@SuppressWarnings("resource")
    		ServerSocket serverSocket = new ServerSocket(port);
    		Integer affluence = 0;
    		while(true){
    			new Server(serverSocket.accept(), affluence);
    			affluence++;
    			System.out.println("nouveau connecter");
    		}
    	} catch(Exception e){}
    }
    -Donc là, j'attend une connexion pour pouvoir créé un new Server avec un autre constructeur que précédemment.
    J'y reviens plus tard, je commence par le Client:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Client client = new Client("localhost", 1234);
    -Le constructeur du client:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public Client(String InetAdress, int port){
    	try{
    		this.socket = new Socket(InetAdress, port);
    		outputStream = socket.getOutputStream();
    		clientListener = new ClientListener(socket);
    	}catch(Exception e){ System.err.println("[Client OutputStream] " + e.getMessage()); } 
    }
    -Le constructeur du ClientListener:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public ClientListener(Socket socket) {
    	this.socket = socket;
    	try {
    		inputStream = this.socket.getInputStream();
    	}catch(Exception e){ System.err.println("[ClientListener InputStream] " + e.getMessage()); } 
    	thread = new Thread(this);
    	thread.start();
    }
    -Le run du ClientListener:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void run(){
    	while(true){
    		try (ObjectInputStream in = new ObjectInputStream(inputStream);){ 
    			final Exchange exchange = (Exchange) in.readObject();
    			if(exchange instanceof Message){ 
    				System.out.println("[ClientListener] " + ((Message) exchange).getMessage());
    			}
    		}catch(Exception e){ System.err.println( "[ClientListener ObjectInputStream] " + e.getMessage() ); System.exit(0); } 
    	}
    }
    -Et du coup, après le new Client je fait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    //Client client = new Client("localhost", 1234);
    client.send(new Message("Message", 0));
    -La méthode send de Client:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void send(Exchange exchange){
    	try (ObjectOutputStream out = new ObjectOutputStream(outputStream)){
    		out.writeObject(exchange);
    	}catch (Exception e) { System.err.println("[Client ObjectOutputStream] " + e.getMessage()); }
    }
    On en reviens au run de Server au moment ou le Client se connecte:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void run(){
    	while(true){
    		try (ObjectInputStream in = new ObjectInputStream(inputStream);){ 
    			final Exchange exchange = (Exchange) in.readObject();
    			if(exchange instanceof Message){ 
    				this.traitement(exchange);
    			}
    		}catch (Exception e) { System.err.println("[Server ObjectInputStream] " + e.getMessage()); System.exit(0); }  
    	} 
    }
    Jusque là tout va bien, le client se connecte et le serveur reçois bien l'objet exchange.
    Mais au moment ou je fait ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private void traitement(Exchange exchange){
    	if(exchange instanceof Message){ 
    		System.out.println("[Server.listener()] " + ((Message) exchange).getMessage());
    		this.send((Exchange)exchange);
    	}
    }
     
    private void send(Exchange exchange){
     
    	for(Integer integer : ((Message)exchange).getReceiver()){
    		if(!connected.get(integer).isConnected()) break;
    		try (Socket socket = connected.get(integer);){
    			try (OutputStream outputStream = socket.getOutputStream();){
    				try (ObjectOutputStream out = new ObjectOutputStream(outputStream);){
    					out.writeObject(exchange);
    					out.flush();
    				}catch (Exception e) { System.err.println("[Server Socket] " + e.getMessage()); } 
    			}catch (Exception e) { System.err.println("[Server OutputStream] " + e.getMessage()); } 
    		}catch (Exception e) { System.err.println("[Server ObjectOutputStream] " + e.getMessage()); } 
    	}
    }
    Je reçois ces erreurs:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [Server Socket] Software caused connection abort: socket write error
    [Server ObjectInputStream] socket closed
    Pas moyen de trouver la panne.

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Pecose Voir le message
    -Je créé un serveur qui maintenant implémente Runnable:
    -le constructeur de Server:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public Server(int port){ 
    	new ServerLauncher(port); 
    }
    -Le constructeur de ServerLauncher:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public ServerLauncher(int port){ 
    	this.port = port; 
    	this.start();
    }
    Quel intérêt ici d'avoir deux classes, surtout que la première ne fait que créé une instance de l'autre dans son constructeur sans même conserver sa référence !

    En plus, c'est incohérent avec ce que je vois là :
    Citation Envoyé par Pecose Voir le message
    -Le run de ServerLauncher:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void run(){
    	try {
    		@SuppressWarnings("resource")
    		ServerSocket serverSocket = new ServerSocket(port);
    		Integer affluence = 0;
    		while(true){
    			new Server(serverSocket.accept(), affluence);
    			affluence++;
    			System.out.println("nouveau connecter");
    		}
    	} catch(Exception e){}
    }


    Citation Envoyé par Pecose Voir le message
    -Le run du ClientListener:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void run(){
    	while(true){
    		try (ObjectInputStream in = new ObjectInputStream(inputStream);){ 
    			final Exchange exchange = (Exchange) in.readObject();
    			if(exchange instanceof Message){ 
    				System.out.println("[ClientListener] " + ((Message) exchange).getMessage());
    			}
    		}catch(Exception e){ System.err.println( "[ClientListener ObjectInputStream] " + e.getMessage() ); System.exit(0); } 
    	}
    }
    try-with-resource, donc fermeture du flux dans un while true. Tu penses bien qu'à la deuxième itération la lecture dans un flux fermé est problématique. Ne cherche pas à fermer les flux des sockets, ferme les sockets, ce qui fermera les flux. Attention de prévoir que le client pouvant se déconnecter sans demander l'avis du serveur, ceci provoquera une exception lors de la lecture du flux par le serveur, et donc un System.exit(0), et fera donc tomber ton serveur !

    [edit]
    Quel intérêt d'utiliser un Integer et pas un int ? Je me doute de ta réponse, je pose juste la question pour t'interpeller sur le fait que ce n'est pas comme ça que tu feras ce que tu cherches à faire, et que si ce n'est pas ce que je pense, alors autant utiliser un int.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  5. #5
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Autre détail qui a son importance: les object outputstream dans ton cas c'est mal barré. Tu essaie de créer plusieurs object outputstream l'un derrière l'autre sur le même stream. Mais l'objectInputstream, de son coté, il est pas du tout prévu pour ça. Il fait des lecture à l'avance etc. Donc il va tomber sur le header de ton deuxième object ouptustream qui suit le premier, au mieux il va prendre ça pour un object, au pire il va te chier une exception. Soit tu met en place un système de multiplexage dans tes Stream (identifier quelle partie du flux correspond à un object stream et balancer uniquement ça à ton ObjectStream), soit tu oublie cette technique complètement.

    J'éviterais, d'une manière générale, d'utiliser des objectoutputstream/ inputstream pour le transfert client serveur. En plus de la complexité de mise en oeuvre, ça pose de gros problème de sécurité. Genre si au lieu de t'envoyer un Message, je t'envoie un graphe complet d'objets java qui ont des effets de bord dans ton système.

    Je passerais par de la sérialisation style JaxB, en précédant chaque message envoyé de sa taille que tu puisse récupérer ça dans un byte puis seulement le décoder.

  6. #6
    Membre éclairé Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    311
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 311
    Par défaut
    Joel, tu me dit:
    "Quel intérêt ici d'avoir deux classes, surtout que la première ne fait que créé une instance de l'autre dans son constructeur sans même conserver sa référence !"
    L'intérêt c'est d'avoir à faire "new Server()" et pas "new ServerLauncher()" pour créé un serveur de manière à ce que ce soit plus facile a retenir, et que si j'ai à envoyer un message depuis le serveur, je n'ai pas à faire "serverLauncher.send()" mais tout simplement "server.send()". Et c'est la structure qui m'est venu spontanément.
    A thermes, si je trouve mieux je changerai ça.

    Quel intérêt d'utiliser un Integer et pas un int ?
    En fait je savais pas qu'il y avait une différence. Maintenant je sais, merci.

    try-with-resource, donc fermeture du flux dans un while true. Tu penses bien qu'à la deuxième itération la lecture dans un flux fermé est problématique. Ne cherche pas à fermer les flux des sockets, ferme les sockets, ce qui fermera les flux. Attention de prévoir que le client pouvant se déconnecter sans demander l'avis du serveur, ceci provoquera une exception lors de la lecture du flux par le serveur, et donc un System.exit(0), et fera donc tomber ton serveur !
    Merci, je crois bien que c'est ça mon problème.

    tchize_ tu me dit:
    Je passerais par de la sérialisation style JaxB, en précédant chaque message envoyé de sa taille que tu puisse récupérer ça dans un byte puis seulement le décoder.
    En fait je ne veux pas forcement transmettre un message, je veux pouvoir transmettre n'importe quelle objet qui appartienne à mon projet.
    Et non, malheureusement le RMI ne convient pas à ce que je voudrai faire.
    Pas techniquement, parce que, oui, ça marche très bien.
    Mais j'aimerais simplifier encore plus le truc.
    Alors j'ai bien pensé qu'au niveau de la sécurité ça serait tendu, mais est-ce que le if(instenceOf) ne garanti pas que l’objet viens bien de "chez moi"?

    Autre détail qui a son importance: les object outputstream dans ton cas c'est mal barré. Tu essaie de créer plusieurs object outputstream l'un derrière l'autre sur le même stream. Mais l'objectInputstream, de son coté, il est pas du tout prévu pour ça. Il fait des lecture à l'avance etc. Donc il va tomber sur le header de ton deuxième object ouptustream qui suit le premier, au mieux il va prendre ça pour un object, au pire il va te chier une exception. Soit tu met en place un système de multiplexage dans tes Stream (identifier quelle partie du flux correspond à un object stream et balancer uniquement ça à ton ObjectStream), soit tu oublie cette technique complètement.
    Alors là, je sais pas du tout ce qu'est le multiplexage.
    J'ai demander à l'autre gogole "multiplexage java" j'ai rien trouver.
    Est-ce que tu pourrai m'aiguiller, je suis curieux de comprendre ce mot qui me pique les yeux.

    Merci de votre aide messieurs.

  7. #7
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par Pecose Voir le message
    Alors j'ai bien pensé qu'au niveau de la sécurité ça serait tendu, mais est-ce que le if(instenceOf) ne garanti pas que l’objet viens bien de "chez moi"?
    Non, quand l'objet t'es retourné, même si tu va le rejeter avec le instanceOf, c'est déjà trop tard. Et comme tu n'as aucun contrôle sur le range d'objet créés, ça peut être n'importe quoi provenant d'une librairie de ton programme et qui aurait des effets de bord exploitable.
    https://www.owasp.org/index.php/Dese...untrusted_data


    Citation Envoyé par Pecose Voir le message
    Alors là, je sais pas du tout ce qu'est le multiplexage.
    C'est juste un terme générique qui veux dire mettre plusieurs flux dans un seul. Exemple avec ton code et en faisant un multiplexage avec un seul flux à la fois, tu dois "en gros" faire ça

    transformer l'objet que tu veux écrire en byte[] (ex dans ton cas en construisant un ObjectOutputStream au dessus d'un ByteArrayOutputStream)
    envoyer dans le flux de la socket la taille du tableau puis son contenu

    de l'autre coté
    réceptionner la taille du tableau, le créer et y mettre les données venant du flux
    Transformer ce byte[] en un objet que tu peux utiliser (ex Mettre tout ça dans un ByteArrayInputStream et construire l'ObjectInputStream dessus)


    Je ne suis pas non plus sur que tu veuille laisser le client envoyer 'n'importe quel objet de ton projet', ça risque vite d'être le bordel dans le code. Il est préférable d'avoir une modèle de tous les messages que tes instances peuvent s'échanger. C'est plus propre, plus facile à sécuriser, plus facile à gérer.

  8. #8
    Membre éclairé Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    311
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 311
    Par défaut
    Zut! Je suis mal barré du coup.
    Dernière chance avant que je n'abandonne mon idée.
    Est il techniquement possible de sécurisé un ObjectInputStream?
    Parce que visiblement, d'après l'article que tu m'as envoyé, c'est pas gagné.
    Et si c'est possible, comment faire?

  9. #9
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 577
    Par défaut
    Ben cet article montre indique comment faire... Et effectivement c'est pas gagné. Énormément plus compliqué que juste utiliser autre chose que ObjectInputStream.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Server Multi-Thread Capricieu
    Par Pecose dans le forum Débuter avec Java
    Réponses: 4
    Dernier message: 09/02/2018, 22h03
  2. Tri multi-threadé
    Par Tifauv' dans le forum C
    Réponses: 8
    Dernier message: 28/06/2007, 09h00
  3. Réponses: 16
    Dernier message: 30/01/2004, 11h05
  4. [VB6][active x] faire du multi-thread avec vb
    Par pecheur dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 20/05/2003, 12h01
  5. [Kylix] exception qtinft.dll et multi-threading
    Par leclaudio25 dans le forum EDI
    Réponses: 3
    Dernier message: 27/03/2003, 18h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo