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

Python Discussion :

Superposition de messages lors de l'envoi via un socket


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    84
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Novembre 2009
    Messages : 84
    Par défaut Superposition de messages lors de l'envoi via un socket
    Bonjour,

    J'ai réalisé un tchat basique fonctionnant sur le protocole TCP/IP avec une architecture clients/serveur (en PyQt, mais ceci n'a pas d'importance ici).

    Le client est composé d'un QTextEdit où j'affiche la conversation, d'un QListWidget qui affiche la liste des personnes connectés, et enfin d'un QLineEdit (et d'un bouton) pour envoyer les messages au serveur qui s'occupe de renvoyer le message à tout le monde.

    Ca ressemble à ça :


    Voici le code de mon serveur :
    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    # -*- coding: utf-8 -*-
     
    import sys, socket
    import threading
     
     
    class Server:
        def __init__(self, address):
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.bind(address)
            self.socket.listen(5)
     
            self.clients = []
            self.pseudos = []
     
        def wait_for_clients(self):
            while True:
                client, address = self.socket.accept()
                self.clients.append(client)
                t = ListeningThread(self.clients, client, self.pseudos)
                t.start()
     
     
    class ListeningThread(threading.Thread):
        def __init__(self, clients, client, pseudos):
            threading.Thread.__init__(self)
            self.clients = clients
            self.client = client
            self.pseudos = pseudos
     
        def run(self):
            name = self.client.recv(1024)
            print type(name)
            address = self.client.getsockname()
            message = "<font color='gray'><b>" + name + "</b> est connecté</font>"
            if len(self.pseudos) > 0:
                self.client.send("MAJsuperman " + str(','.join(self.pseudos)))
            self.send_to_all(message)
            self.pseudos.append(name)
            self.send_to_all("JOINsuperman " + name)
            while True:
                message = self.client.recv(1024)
                if message == "":
                    self.clients.remove(self.client)
                    message = "<font color='gray'><b>" + name + "</b> s'est déconnecté</font>"
                    self.send_to_all("LEFTsuperman " + name)
                    self.pseudos.remove(name)
                    self.send_to_all(message)
                    break
                message = "<b>" + name + "</b> : " + message
                self.send_to_all(message)
     
        def send_to_all(self, message):
            print message
            for client in self.clients:
                client.send(message)
     
    if len(sys.argv) != 3:
        print """Usage: python server.py ip port
    example: python server.py localhost 54321"""
        sys.exit()
     
    address = sys.argv[1], int(sys.argv[2])
    server = Server(address)
    server.wait_for_clients()
    Comme vous pouvez le voir, j'utilise un message "spécial" pour indiquer aux clients la connexion ou la déconnexion d'une personne pour y afficher (ou retirer) son pseudo dans la QListWidget.

    Cependant, lorsque je fais appel à la méthode send_to_all 2 fois à la suite, du côté du client je ne reçois pas 2 messages séparément, mais je reçois un seul message avec les 2 messages concaténés...

    Concrètement côté serveur j'envoie :
    - "<font color='gray'><b>" + name + "</b> est connecté</font>" d'une part, et :
    - "JOINsuperman " + name d'autre part

    et côté client, au lieu de recevoir ces messages séparément afin d'afficher le premier message dans le QTextEdit, et de traiter le second message en affichant le pseudo dans la QListWidget, je reçois les 2 messages ensemble en une seule fois...
    <font color='gray'><b>" + name + "</b> est connecté</font>JOINsuperman mon_pseudo

    A noter que ceci est aléatoire, parfois ça fonctionne, et souvent ce phénomène ce produit... (seulement lors de la connexion/déconnexion vu que je fais plusieurs send() en suivant). Lors d'une discussion tout fonctionne bien.

    Si besoin, voici la méthode côté client qui s'occupe de recevoir les messages et de les traiter en fonction :
    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
    def run(self):
            while self.running:
                message = self.sock.recv(1024)
                print 'message recu: ' + message
                if message[0:11] == "MAJsuperman":
                    pseudos = message[12:]
                    self.emit(SIGNAL("maj"), pseudos)
                elif message[0:12] != "JOINsuperman" and message[0:12] != "LEFTsuperman" and message[0:11] != "MAJsuperman":
                    self.emit(SIGNAL("message_recu"), message)
                elif message[0:12] == "JOINsuperman":
                    pseudo = message[13:]
                    self.emit(SIGNAL("join"), pseudo)
                elif message[0:12] == "LEFTsuperman":
                    pseudo = message[13:]
                    self.emit(SIGNAL("left"), pseudo)
     
                QApplication.processEvents()
    Sauriez-vous comment je pourrais y remédier ?

    Merci d'avance !

  2. #2
    Membre émérite
    Homme Profil pro
    Ingénieur R&D en apprentissage statistique
    Inscrit en
    Juin 2009
    Messages
    447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur R&D en apprentissage statistique

    Informations forums :
    Inscription : Juin 2009
    Messages : 447
    Par défaut
    Je n'ai pas lu trop en détail ton code mais est-ce que cela ne viendrait pas du fait que tu n'a pas de "parser" de message.

    D'après ce que j'ai compris tu lis à chaque boucle le contenu de la socket. La plupart du temps cela fonctionne parce que le temps d'envoie entre deux message et plus long que le temps de passage dans la boucle mais dans le cas ou les deux messages sont envoyés à la suite rapidement ils se retrouvent tous les deux dans en file d'attente.

    Si c'est bien de cela qu'il s'agit, tu peux faire un tampon qui lit le contenu de la socket et retourne un message lorsqu'un marqueur de fin de message est présent.

    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
    class MessageBuffer :
        def __init__(sell,socket):
            self.socket = socket
            self.buffer = ""
     
        def find_message(self):
              """ extrait le dernier message du buffer si possible, renvoie
                   None sinon et met à jour le tampon.
              """
              # le code qu'il faut
     
         def next(self):
              buffer+=self.sock.recv(1024)
              message = self.find_message()
              if message is not None :
                     yield message
    Il s'agit d'un générateur (utilisation de yield) que tu peux ensuite utiliser comme ç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
    def run(self):
            for message in self.message_buffer() :
                print 'message recu: ' + message
                if message[0:11] == "MAJsuperman":
                    pseudos = message[12:]
                    self.emit(SIGNAL("maj"), pseudos)
                elif message[0:12] != "JOINsuperman" and message[0:12] != "LEFTsuperman" and message[0:11] != "MAJsuperman":
                    self.emit(SIGNAL("message_recu"), message)
                elif message[0:12] == "JOINsuperman":
                    pseudo = message[13:]
                    self.emit(SIGNAL("join"), pseudo)
                elif message[0:12] == "LEFTsuperman":
                    pseudo = message[13:]
                    self.emit(SIGNAL("left"), pseudo)
     
                QApplication.processEvents()
    à toi de déterminer quel marqeur de fin de message tu souhaites, cela peut être tout simplement un "\n".

    En espérant que cela aide.

    Alexis

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    84
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Novembre 2009
    Messages : 84
    Par défaut
    Merci pour ta réponse, mais je n'ai pas réussi à le mettre en place...

    Du coup j'avais mis un time.sleep(0.3) entre chaque send() et ça fonctionnait... Mais c'est laid comme façon de coder et pas optimal.

    Alors j'ai tout simplement mis un "\n" à la fin de chaque send(message + "\n")
    Et sur le client :
    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
    while self.running:
                messages = self.sock.recv(1024)
     
                for message in messages.splitlines():
                    print 'message recu: ' + message
     
                    if message[0:11] == "MAJsuperman":
                        pseudos = message[12:]
                        self.emit(SIGNAL("maj"), pseudos)
                    elif message[0:12] != "JOINsuperman" and message[0:12] != "LEFTsuperman" and message[0:11] != "MAJsuperman":
                        self.emit(SIGNAL("message_recu"), message)
                    elif message[0:12] == "JOINsuperman":
                        pseudo = message[13:]
                        self.emit(SIGNAL("join"), pseudo)
                    elif message[0:12] == "LEFTsuperman":
                        pseudo = message[13:]
                        self.emit(SIGNAL("left"), pseudo)
     
                    QApplication.processEvents()
    Ca à l'air de fonctionner également, est-ce que c'est une bonne solution ?


    Cela dit, je ne comprends quand même pas pourquoi 2 send() se retrouvent dans la même variable à l'arrivée (recv(1024)).

  4. #4
    Membre émérite
    Homme Profil pro
    Ingénieur R&D en apprentissage statistique
    Inscrit en
    Juin 2009
    Messages
    447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur R&D en apprentissage statistique

    Informations forums :
    Inscription : Juin 2009
    Messages : 447
    Par défaut
    Ta socket c'est juste un tampon qui se remplit quand des données sont reçues et se vide lorsque tu la lit, il est donc normal que si deux messages sont envoyés le temps d'une boucle, ils se retrouvent concaténés.

    Donc faire un sleep et/ou ajouter une "\n" n'est pas une réelle solution. Cela peut fonctionner en pratique si les messages ne sont pas envoyés trop rapidement mais en théorie tu risque toujours le télescopage.

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    84
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Novembre 2009
    Messages : 84
    Par défaut
    Ah d'accord. Mais le fait de rajouter un \n en fin de chaine, même s'il y a telescopage la méthode splitlines() dans le client me permet quand même d'avoir le résultat voulu en toute sécurité ?

  6. #6
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    hmmm... '\n' est quand même hyper commun comme caractère... tu peux établir un protocle de communication sur deux bytes (ou plutôt deux caractère):
    Le premier byte servirait à indiquer le début d'un ensemble (commande+données), le byte suivant indiquerait l'instruction (ou le type de donnée, ce que tu veux en fait). Ainsi, dans le cas deux envois concaténés, il reste très facile avec une regex de récupérer une liste de tuples (instruction,données)... dans ton cas précis, on pourrait se contenter d'un simple byte de séparation, mais l'utilisation de deux bytes offre de nouvelles possibilités (par exemple du téléchargement, un instruction d'affichage, etc...). Comprends-tu ?

    NB: ah oui, un bon premier caractère (indiquant le début d'un ensemble commande+donnée), peut être '\x00' pour la version encodée, il fonctionne quelque soit l'encodage (ainsi qu'en unicode évidement)

Discussions similaires

  1. [XL-2007] VBA EXCEL Choisir la boite mail d'envoi lors d'un envoi via Outlook
    Par Joeytriviani dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 11/04/2014, 13h57
  2. [OL-2007] Suppression des messages lors de l'envoi
    Par jpetmmd dans le forum Outlook
    Réponses: 1
    Dernier message: 10/11/2011, 17h32
  3. erreur lors de l'envoie de donné via les socket.
    Par poporiding dans le forum C++
    Réponses: 1
    Dernier message: 23/05/2006, 14h23
  4. A respecter lors de l'envoi d'un message (Version 2)
    Par Aurelien.Regat-Barrel dans le forum Windows
    Réponses: 0
    Dernier message: 04/01/2003, 15h59

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