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

  1. #1
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut variable persistante entre plusieurs programmes différentes

    Bonjour à tous,

    Ça fait plusieurs mois que je planche sur un problème sans trouver de solution efficace. Je cherche donc des conseils.

    • J'ai un programme principal qui reçoit des taux en chiffre sous format dictionnaire depuis un serveur distant. (script_socket_client.py).
    • J'ai plusieurs programmes dans des scripts différents (script_client.py) qui doivent pouvoir lire ces données mise à jours par ce programme principal.



    J'ai testé notamment le stockage de valeur via un fichier shelve. Mais l’inconvénient c'est que l'on ne peut pas lire et écrire en même temps.

    Au final le but serait de pouvoir stoker mon dictionnaire dans un variable commune à tous les autres programmes accessible en lecture.

    Mais comment faire !

    Je vous remercie pour vos conseils et remarque.

  2. #2
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    août 2008
    Messages
    24 439
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : Enseignement

    Informations forums :
    Inscription : août 2008
    Messages : 24 439
    Points : 161 554
    Points
    161 554

    Par défaut



    Si tes programmes ne fonctionnent pas en simultané, tu peux passer par un fichier Pickle, par exemple : tous tes programmes en ont le chemin, ils lisent/écrivent selon leurs besoins ; maintenant, ça ne semble pas tellement correspondre à ta situation. Sinon, il faut passer par de la mémoire partagée ou d'autres mécanismes de communications entre processus : https://docs.python.org/3/library/ipc.html. Vu ton cas d'utilisation, ne pourrais-tu pas envisager une base de données (pas SQLite, plutôt MySQL, H2, SQL Server Compact, histoire d'avoir des accès concurrents à la base de données) ?
    Vous souhaitez participer aux rubriques Qt ou PyQt (tutoriels, FAQ, traductions), HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  3. #3
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    bonsoir dourouc

    j'étais justement en train de me pencher sur la solution sqlite fourni nativement pas python, mais si je comprend bien tu le le déconseilles? ça ne supporte pas la lecture et écriture simultané ?

    concernant pickle, si le fonctionnement est identique à shelve (impossible de lire et d'écrire en même temps par 2 programmes différents) ça va être compliqué.
    je vais lire ton lien


    je te remercie pour tes conseils.

    Bonne soirée

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Bonjour,

    Citation Envoyé par tintin3158 Voir le message
    j'étais justement en train de me pencher sur la solution sqlite fourni nativement pas python, mais si je comprend bien tu le le déconseilles? ça ne supporte pas la lecture et écriture simultané ?
    Effectivement, sqlite ne supporte pas les accès concurrents.

    Tu peux aussi utiliser postgresql qui est un très bon SGBDR open source: https://www.postgresql.org/
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    Citation Envoyé par tyrtamos Voir le message
    Bonjour,



    Effectivement, sqlite ne supporte pas les accès concurrents.

    Tu peux aussi utiliser postgresql qui est un très bon SGBDR open source: https://www.postgresql.org/


    Bonjour tytamos,

    Bon effectivement si sqlite ne supporte pas les mutiples accès je part encore dans la mauvaise direction.
    un solution serait également de revoir mon code et d'utiliser les trhead. ce qui me fait un peu peur car ça à l'air compliqué.

    Mais je me demande avec les trhead,:
    • peux ton lancer un trhead pour en lancer un second (ça oui)
    • peux ton récupérer via un menu par exemple le second trhead pour agir dessus (l'arrêter ou le relancer je suppose)



    ça me permettrait de pouvoir lancer mon programme d'une façon plus optimiser.

    Je vous remercie pour votre temps et vos conseils !

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Bonjour,

    Je ne suis pas sûr de bien comprendre tout ton contexte, mais les threads pourraient être une solution si tous tes programmes pouvaient faire partie d'un seul et même programme.

    Dans ce cas:

    - le programme est lancé, et il commence à créer un thread dont l'unique fonction sera de récupérer et d'enregistrer les données dans une variable globale, protégée par un verrou (threading.Lock).

    - après avoir fait ça, le programme affiche un menu et attend qu'un utilisateur lui demande quelque chose (fonction "client"). Ce programme pourra accéder à la variable globale en utilisant le verrou pour éviter les collisions d'accès. Il n'est pas nécessaire de créer un autre thread pour ça.

    Mais si les programmes doivent rester séparés, il ne reste plus, à mon avis, qu'un SGBD ou une autre solution du même genre à trouver. Si c'est un fichier, il faut trouver l'analogue d'un verrou pour empêcher sa lecture en même temps que sa mise à jour.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  7. #7
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    Effectivement ce que décrit semble la solution la plus propre.
    En terme de fonctionnalité j'ai :

    • Un programme principal dans lequel je saisi des ordres d'achat ou de vente en fonction d'une monnaie
    • Un programme secondaire (socket) qui reçoit en temps réel les taux de la monnaie.
    • un autre programme secondaire (socket) qui reçoit le statu de mes transactions (ordre acheté, vendu....)



    Le point bloquant. C'est que lorsque je lance un nouvel ordre d'achat pour une autre monnaie (ou action) je dois arrêter le socket qui reçoit les cours pour ajouter cette nouvelle monnaie et le relancer. (Actuellement, j'un un socket de lancé par action ce qui est pas très optimisé, mon idée est de lancer un seul socket pour l'ensemble des taux à récupérer.)

    Je ne sais pas si c'est plus clair.
    Mais notre conversation me fait gagner du temps et m'évite de passer du temps dans une mauvaise direction.

  8. #8
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Bonjour,

    J'ai peut-être une autre idée de solution.

    En gardant la solution avec plusieurs programmes différents, il me semble possible de créer un serveur TCP. Voilà comment cela pourrait marcher:

    1- on crée un serveur TCP multithread, qui au lancement commencera par créer un thread supplémentaire, dont l'objectif sera d'aller chercher les données et de les enregistrer dans une variable globale protégée par un verrou threading.Lock. Pour le reste de son activité, il fera le boulot de serveur, c'est à dire attendra une requête, la traitera et renverra le résultat au client qui l'a demandé.

    2- on crée un ou plusieurs client(s) qui pourront demander les infos au serveur

    Il faut, bien sûr, prévoir un ensemble de correspondance requete <=> traitement pour que le serveur comprenne bien ce qu'on lui demande.

    Pour illustrer cette idée, voilà un exemple de code serveur <=> client, qui va simplement faire des calculs d'expressions avec eval(...). Chaque nouvelle requête d'un client sera reçue dans un nouveau thread, sera traitée, et le résultat sera renvoyé au client:

    serveur.py:

    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
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    """
    Serveur TCP multithread utilisant StreamRequestHandler du module SocketServer
    Chaque client reçu sera traité dans un nouveau thread
    Ici: exemple de calcul d'expression par eval(...)
    """
     
    import socketserver
     
    from math import * # nécessaire pour l'exemple (=calcul d'expression)
     
    ##############################################################################
    class Traitementrequete(socketserver.StreamRequestHandler):
     
        #=========================================================================
        def handle(self):
     
            # lit la requête
            requete = self.rfile.readline()
            # convertit en str pour traitement
            requete = str(requete, encodage).rstrip()
     
            """
            # pour mise au point:
            print("{} demande:".format(self.client_address[0]))
            print(requete)
            print()
            """
     
            # prépare la réponse
            try:
                reponse = str(eval(requete))
            except Exception as msgerr:
                reponse = "Erreur: " + str(msgerr)
     
            # convertit la réponse en bytes
            reponse = bytes(reponse, encodage)
     
            # envoie la réponse au client
            self.wfile.write(reponse)
     
            return
     
    ##############################################################################
    class Serveurthread(socketserver.ThreadingMixIn, socketserver.TCPServer):
     
        allow_reuse_address = True
     
    ##############################################################################
    if __name__ == '__main__':
     
        adresse=('localhost', 20000)
        encodage = 'cp850' # encodage='cp850' pour la console Windows
     
        with Serveurthread(adresse, Traitementrequete) as serveur:
            print("serveur actif sur l'adresse:", serveur.server_address)
            serveur.serve_forever()
    client.py:

    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
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    """
    client TCP
    """
     
    import socket
     
    ##############################################################################
    if __name__ == '__main__':
     
        buf=65536 #1024 => à adapter aux données à recevoir!
        adresse=('localhost', 20000)
        encodage = 'cp850' # encodage='cp850' pour la console Windows
     
        while True:
     
            # saisie la requete au clavier
            requete = input("?: ").strip()
     
            if requete=="":
                print("arret du client TCP")
                break # arrête le client
            else:
                # établit une nouvelle connexion
                maSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    maSocket.connect(adresse)
                except:
                    print("connexion impossible")
                    break # arrête le client
     
                # envoie la requête au serveur
                envoi = maSocket.send(bytes(requete + '\n', encodage))
     
                # réceptionne la réponse du serveur
                reponse = str(maSocket.recv(buf), encodage).rstrip()
     
                # affiche la réponse
                print(reponse)
     
                # arrête la connexion
                maSocket.shutdown(socket.SHUT_RDWR)
                maSocket.close()
    Pour essayer cet exemple:

    - on ouvre une console pour le serveur, on se place dans le répertoire du code serveur.py, on lance, et il devrait s'afficher:
    serveur actif sur l'adresse: ('127.0.0.1', 20000)
    - on ouvre une console pour le client, on se place dans le répertoire du code client.py, on lance, et il devrait s'afficher:
    ?:
    Si le client demande: "2*3", le serveur répondra: "6" (heureusement! ;-) )
    Si le client demande: "sin(0.5)", le serveur répondra: "0.479425538604203"
    Si le client demande: "1/0" (=division par zéro), le serveur répondra: "Erreur: division by zero"

    On peut adapter la taille du buffer côté client au type de données à recevoir. Par exemple:
    avec buf = 65536, le client peut demander: factorial(15000), qui renverra un résultat composé de... 56130 chiffres!

    En adaptant ce code, tu devrais pouvoir traiter ton problème.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  9. #9
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    7 194
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 7 194
    Points : 20 497
    Points
    20 497
    Billets dans le blog
    1

    Par défaut

    Bonjour

    Ne serait-ce pas que de réinventer la roue de faire ce serveur ? Je pense pour ma part que ta précédente solution d'utiliser une bdd libre (Postgres, MySQL) serait le plus simple. C'est déjà fait, c'est pas compliqué à interfacer depuis Python et ça supporte tout à fait les accès concurrents. Et ça offre aussi une ouverture pour le futur (on ne sait pas trop comment peut évoluer ce programme)...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  10. #10
    Modérateur

    Homme Profil pro
    Architecte technique
    Inscrit en
    juin 2008
    Messages
    13 782
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte technique
    Secteur : Industrie

    Informations forums :
    Inscription : juin 2008
    Messages : 13 782
    Points : 23 208
    Points
    23 208

    Par défaut

    Salut,

    Citation Envoyé par Sve@r Voir le message
    Ne serait-ce pas que de réinventer la roue de faire ce serveur ? Je pense pour ma part que ta précédente solution d'utiliser une bdd libre (Postgres, MySQL) serait le plus simple.
    Une base de donnée s'imposerait s'il y avait des quantités importantes d'informations à stocker genre historique des transactions. Là pour l'instant, nous avons une interface utilisateur (qui devrait être asynchrone) qui échange de petits messages avec d'autres activités asynchrones (pouvant être réalisées via un ou plusieurs threads).
    En soit rien de très compliqué, juste que "asynchrone" demande une certaine expertise.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    7 194
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 7 194
    Points : 20 497
    Points
    20 497
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par tyrtamos Voir le message
    Code python : 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
    serveur.py:
    
    ##############################################################################
    class Serveurthread(socketserver.ThreadingMixIn, socketserver.TCPServer):
    
        allow_reuse_address = True
        
    ##############################################################################
    if __name__ == '__main__':
     
        adresse=('localhost', 20000)
        encodage = 'cp850' # encodage='cp850' pour la console Windows
     
        with Serveurthread(adresse, Traitementrequete) as serveur:
            print("serveur actif sur l'adresse:", serveur.server_address)
            serveur.serve_forever()
    J'ai voulu tester mais ça me met une erreur "AttributeError: __exit__" sur la ligne with Serveurthread(adresse, Traitementrequete) as serveur. Il me semble que la classe "Serveurthread" est un peu light pour être utilisée comme context manager (manque les méthodes "__enter__" et "__exit__" d'où l'erreur remontée)...

    Le client fonctionne mais bien entendu sans serveur pour lui répondre...

    Citation Envoyé par wiztricks Voir le message
    Une base de donnée s'imposerait s'il y avait des quantités importantes d'informations à stocker genre historique des transactions. Là pour l'instant, nous avons une interface utilisateur (qui devrait être asynchrone) qui échange de petits messages avec d'autres activités asynchrones (pouvant être réalisées via un ou plusieurs threads).
    Oui certes. Une bdd Postgres c'est riche. Tout dépend du besoin actuel, de l'investissement qu'on veut y donner (installer/configurer postgres, créer la bdd, les comptes, etc) et du besoin futur. Bref c'est à réfléchir et à étudier le ratio temps investi/résultats
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  12. #12
    Modérateur

    Homme Profil pro
    Architecte technique
    Inscrit en
    juin 2008
    Messages
    13 782
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Architecte technique
    Secteur : Industrie

    Informations forums :
    Inscription : juin 2008
    Messages : 13 782
    Points : 23 208
    Points
    23 208

    Par défaut

    Citation Envoyé par Sve@r Voir le message
    Oui certes. Une bdd Postgres c'est riche.
    C'est riche vis a vis des besoins exprimés et peut être même aussi par rapport aux connaissances en SGDB du PO.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  13. #13
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Bonjour Sve@r,

    Citation Envoyé par Sve@r Voir le message
    J'ai voulu tester mais ça me met une erreur "AttributeError: __exit__" sur la ligne with Serveurthread(adresse, Traitementrequete) as serveur. Il me semble que la classe "Serveurthread" est un peu light pour être utilisée comme context manager (manque les méthodes "__enter__" et "__exit__" d'où l'erreur remontée)...
    Désolé, j'aurais dû le préciser: depuis que je suis passé à la version 3.7, j'en utilise les possibilités. Et j'aime bien le "with" du context manager: je trouve que ça clarifie le code (et ça évite d'oublier les fermetures quand il y en a). En fait, le "with" est permis avec cette classe à partir de Python 3.6, et les exemples de code du module socketserver sont donnés avec: https://docs.python.org/3/library/so...e-socketserver.

    Solution pour <3.6:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        serveur = Serveurthread(adresse, Traitementrequete)
        print("serveur actif sur l'adresse:", serveur.server_address)
        serveur.serve_forever()
    Pour le reste, on ne connait pas suffisamment le contexte du PO pour choisir à sa place. La solution du SGBD est ok, mais nécessite d'installer un gros machin. Et ça me gênait de ne pas avoir trouvé une 2ème solution concurrente. Et puis je suis curieux... (mais non, ce n'est pas un vilain défaut!)
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  14. #14
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    7 194
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 7 194
    Points : 20 497
    Points
    20 497
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par tyrtamos Voir le message
    Et j'aime bien le "with" du context manager: je trouve que ça clarifie le code (et ça évite d'oublier les fermetures quand il y en a).
    Oui, je m'en sers aussi avec abondance. J'avais bien compris ton idée mais me manquait la partie concrète du context manager...

    Citation Envoyé par tyrtamos Voir le message
    En fait, le "with" est permis avec cette classe à partir de Python 3.6
    Compris. Ton objet hérite de deux classes mères et au-moins l'une d'elles a évolué pour autoriser son appel en tant que context manager. Je comprends l'utilité de "socketserver.TCPServer" mais à quoi sert "socketserver.ThreadingMixIn" ? Je vois bien une notion de "thread" et de "mix" mais concrètement ?

    Citation Envoyé par tyrtamos Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        serveur = Serveurthread(adresse, Traitementrequete)
        print("serveur actif sur l'adresse:", serveur.server_address)
        serveur.serve_forever()
    Ok, c'est bon (mais j'eus pensé que tu aurais plutôt rajouté les méthodes "__enter__" et "__exit__" manquantes permettant ainsi aux pauvres arriérés confinés dans leurs versions obsolètes de pouvoir quand-même utiliser le with...)

    Allez, je m'y colle
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Serveurthread(socketserver.ThreadingMixIn, socketserver.TCPServer):
     
    	allow_reuse_address = True
     
    	def __enter__(self):
    		return self
     
    	def __exit__(self, tp, e, trace):
    		self.server_close()
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  15. #15
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Citation Envoyé par Sve@r Voir le message
    Je comprends l'utilité de "socketserver.TCPServer" mais à quoi sert "socketserver.ThreadingMixIn" ?
    ThreadingMixIn est la classe qui permet à chaque nouvelle requête d'être traitée dans un nouveau thread. Si on n'utilise pas cette fonctionnalité, chaque nouvelle requête doit attendre que la précédente soit terminée pour être traitée.

    Ton ajout de __enter__ et __exit__ est ok par rapport au code source du module.


    A part ça, j'ai essayé de me rapprocher du pb du PO, ne serait-ce que pour vérifier que ma solution tient la route.

    J'ai modifié le serveur en ajoutant une classe threading.Thread appelée "Cherchedatas", et qui va lire en tâche de fond périodiquement (toutes les secondes par défaut) la date et l'heure de l'ordinateur, va les mettre sous forme ISO et va les stocker dans la variable globale "dateheure", protégée par un "verrou" de type threading.Lock() (variable globale aussi). Le lancement de cette classe thread se fait au lancement du serveur. A voir comment la mise à jour est faite: le verrou permet d'attendre que l'accès soit libre, puis met le verrou pour interdire d'autres accès pendant la mise à jour et le relâche après. Ce mécanisme utilise le context manager avec "with".

    J'ai ensuite modifié le traitement des requêtes des clients qui se trouve dans la méthode "handle": 3 requêtes sont possibles => "date", heure" et "dateheure". La lecture de la variable globale "dateheure" est faite en utilisant le verrou, et la réponse est renvoyée au client. Si la requête n'est pas reconnue, le serveur renvoie une erreur de syntaxe.

    Voilà le nouveau code du serveur (le client ne change pas):

    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    """
    Serveur TCP multithread utilisant StreamRequestHandler du module SocketServer
    Chaque client reçu sera traité dans un nouveau thread
    Ici: exemple: obtention de la date et/ou de l'heure
    """
     
    import socketserver
     
    # importations nécessaires pour l'exemple
    import threading
    import time
    from math import * 
     
    ##############################################################################
    class Cherchedatas(threading.Thread):
        """thread qui va lire la date et l'heure périodiquement et mettre à jour
        la variable globale "dateheure" protégée par un verrou threading.Lock
        """
     
        #=========================================================================
        def __init__(self, delai=1):
            super().__init__()
     
            self.delai = delai # délai d'attente entre 2 lectures
     
        #=========================================================================
        def run(self):
            global dateheure, verrou
            while True:
                dateiso =  time.strftime('%Y/%m/%d %H:%M:%S')
                with verrou:
                     dateheure = dateiso
                time.sleep(self.delai)
     
    ##############################################################################
    class Traitementrequete(socketserver.StreamRequestHandler):
     
        #=========================================================================
        def handle(self):
            global dateheure, verrou
     
            # lit la requête
            requete = self.rfile.readline()
            # convertit en str pour traitement
            requete = str(requete, encodage).rstrip()
     
            """
            # pour mise au point:
            print("{} demande:".format(self.client_address[0]))
            print(requete)
            print()
            """
     
            # prépare la réponse
            if requete=="date":
                with verrou:
                    reponse = dateheure.split(' ')[0]
            elif requete=="heure":
                with verrou:
                    reponse = dateheure.split(' ')[1]
            elif requete=="dateheure":
                with verrou:
                    reponse = dateheure
            else:
                reponse = "Erreur de syntaxe!"        
     
            # convertit la réponse en bytes
            reponse = bytes(reponse, encodage)
     
            # envoie la réponse au client
            self.wfile.write(reponse)
     
            return
     
    ##############################################################################
    class Serveurthread(socketserver.ThreadingMixIn, socketserver.TCPServer):
     
        allow_reuse_address = True
     
    ##############################################################################
    if __name__ == '__main__':
     
        dateheure = "" # variable globale
        verrou = threading.Lock() # variable globale
        cherchedatas = Cherchedatas(1)
        cherchedatas.daemon = True
        cherchedatas.start()
     
        adresse=('localhost', 20000)
        encodage = 'cp850' # encodage='cp850' pour la console Windows
     
        with Serveurthread(adresse, Traitementrequete) as serveur:
            print("serveur actif sur l'adresse:", serveur.server_address)
            serveur.serve_forever()
    Maintenant, les saisies clavier côté client appellent du serveur les réponses comme ça:

    ?: heure
    20:27:46
    ?: date
    2019/01/20
    ?: dateheure
    2019/01/20 20:27:56
    ?: n'importe quoi
    Erreur de syntaxe!
    Manifestement, le principe a l'air de marcher!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  16. #16
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    bonjour à tous

    dit donc je m'étais tellement lancé sur l'idée du socket serveur que je n'étais pas repassé sur le forum.
    Je vais lire attentivement tous vous commentaires...
    J'ai bien avancé et mon socket client serveur fonctionne (ou presque)
    J'ai quand même une légere contrainte car je ne sais pas trop comment envoyer mes requêtes via une variable (appellé par d'autre partie du programme par exemple) à la place du input clavier.

    Je suis suis pas encore très avancé donc mon code va pour paraitre pas terrible.

    Ce code fonctionne (en python 3.7) par contre je consomme beaucoup de processeur a cause de mes variable globale en émission. de donnée.
    C'est le seul moyen que j'ai trouvé pour envoyer des données durant la boucle en cours .

    Merci pour votre aide.


    voici ma partie 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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
     
    import socket, sys, threading, socketbinance
     
    server_ip = "192.168.0.200"
    server_port = 8090
     
    #TODO paramétrer le timeout du serveur durant coupure
    status = socketbinance.Socket_order_status()
    status.start_socket()
     
     
    class ThreadClient(threading.Thread):
        # class pour géré le thread des connexions client
        def __init__(self, conn):
            threading.Thread.__init__(self)
            self.connexion= conn
     
        def run(self):
            #dialogue avec le client
            nom = self.getName() #chaque thread possède un nom
            #connexion au serveur binance pour récupérer les ordre
            while 1:
                msgclient = self.connexion.recv(1024).decode("Utf8")
                if not msgclient or msgclient.upper() =="FIN":
                    break
                message = "%s > %s" % (nom, msgclient)
                print(message)
                filter = msgclient.split()
     
                #plus utilisé en attente pour l'instant
                #TODO peut être à supprimer si plus utile
                if filter[0] == "getbinanceid":
                    print("rentre dans la condition test binancid")
                    self.connexion.send(str(status.get_status_binance_id(binanceid=filter[1])).encode("Utf8"))#NEW CANCELLED FILLED
     
     
                if filter[0] =="getorderid":
                    print("rentre dans la condition test orderid")
                    self.connexion.send(str(status.get_status_order_id(orderid=filter[1])).encode("Utf8"))  # NEW CANCELLED FILLED
     
     
                # if filter[0] != "getbinanceid" and filter[0] !="getorderid":
                #     self.connexion.send('None'.encode("Utf8"))
     
     
     
                #ACTIVE SUPPRESIONN SI ORDRE EN FILLED OU CANCELLED
                #TODO avoir pour supprimer les ordre qui reste en filled après annulation
                # if status.is_cancel_filled(orderid=msgclient) is True:
                #     print("suppresion de l'ordre :")
                #     status.del_status_order_id(orderid=msgclient)
     
            #Fermeture de la connexion:
            self.connexion.close() # copure la connexion cote serveur
            del conn_client[nom]    #supprmier son entrée dans le dictionnaire
            print("Client %s déconnecté." % nom)
     
    # initialisation du serveur - Mise en place du socket:
    mysocket= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    try:
        mysocket.bind((server_ip, server_port))
    except socket.error:
        print("La liaison du socket à l'adresse choisi a echoué")
        sys.exit()
    print("Serveur pret, en attente de requetes ...")
    mysocket.listen(3)
     
    # Attente et prise en charge des connexions demandées par les clients:
    # dictionnaire des conneions clients
    conn_client={}
    while 1:
        connexion, adresse= mysocket.accept()
        #créer un nouvel objet thread pour gérer la connexion:
        th = ThreadClient(connexion)
        th.start()
        #mémoriser la connexion dans le dictionnaire:
        it = th.getName() #identifiant du thread
        conn_client[it]=connexion
        print("client %s connecté, adressse IP %s, port %s" %\
              (it, adresse[0], adresse[1]))
     
        #dialogue  avec le client:
        msg= "Vous êtes connecté. Envoyé vos messages."
        connexion.send(msg.encode("Utf8"))

    Mon 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
    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     
    import socket, sys, threading
     
     
    server_ip = "192.168.0.200"
    server_port = 8090
     
    #VARIABLE GLOBALE
    RESQUEST ='vide'
    REPSERVEUR='vide'
     
    class ThreadReception(threading.Thread):
        #classe gérant la reception d'un message
        def __init__(self,conn):
            threading.Thread.__init__(self)
            self.connexion = conn # ref du socket de connexion
     
        def run(self):
            global REPSERVEUR
            while 1:
                # message_recu = self.connexion.recv(2048).decode("Utf8")
                REPSERVEUR = self.connexion.recv(2048).decode("Utf8")
     
                # print (dir(self.connexion))
                if not REPSERVEUR or REPSERVEUR.upper() == "FIN":
                    break
            print("Client arrêté. Connexion interrompu")
            self.connexion.close()
     
    class ThreadEmession(threading.Thread):
        # classe gérant l'émission d'un message
        def __init__(self, conn):
            threading.Thread.__init__(self)
            self.connexion = conn  # ref du socket de connexion
     
        def run(self):
            global RESQUEST
            while 1:
                if RESQUEST != 'vide':
                    # message_emis = input()
                    message_emis = RESQUEST
                    self.connexion.send(str(message_emis).encode("Utf8"))
                    if not message_emis or message_emis.upper() == "FIN":
                        break
                    RESQUEST='vide'
            self.connexion.close()#ajouté pour coupure total avec le serveur
     
     
    #programme principal - Etablissement de la connexion
    connexion = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    try:
        connexion.connect((server_ip,server_port))
    except socket.error:
        print("connexion du client échoué")
        sys.exit()
    print("connexion etablie avec le serveur")
     
     
     
    class Status:
        def repserveur(self):
            global REPSERVEUR
            return  REPSERVEUR
        def client(self,orderid):
            global RESQUEST
            RESQUEST=orderid
     
     
     
    #dialogue avec le serveur: on lance deux thread pour gérer
    #indépendamment l'emission et la réception des messages
     
    th_E = ThreadEmession(connexion)
    th_R = ThreadReception(connexion)
    th_E.start()
    th_R.start()
    Citation Envoyé par tyrtamos Voir le message
    Hello merci pour tout ton temps.
    Mon code à moi est bien moins performant. je vais tenter d'adapter avec ce que tu as développé (ça m'a pris pas mal de temps pour y arriver).
    J'ai toujours mon problème pour envoyer directement une variable dans la requête cliente plutôt que un input clavier.
    Je te remercie

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    7 194
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 7 194
    Points : 20 497
    Points
    20 497
    Billets dans le blog
    1

    Par défaut

    Citation Envoyé par tintin3158 Voir le message
    J'ai toujours mon problème pour envoyer directement une variable dans la requête cliente plutot que un input clavier.
    ca semble assez trivial comme manip. L'input clavier te donne une chaine, chaine envoyée dans le socket (envoi = maSocket.send(bytes(requete + '\n', encodage)) dans le client de Tyrtamos). Donc ici, concrètement, on envoie le contenu de la variable "requete". Ca devrait pas poser de souci d'y envoyer une autre variable, pourvu qu'elle contienne une chaine.

    Si maintenant tu veux passer autre chose (donc en fait on tombe là dans le binaire pur) alors soit tu envoies le binaire pur tel quel et à condition qu'il soit récupéré dans un réceptacle identique de l'autre côté ça fonctionnera ; soit tu convertis ton binaire en flux ascii via des outils comme pickle, bin2hex ou base64 (le premier pouvant convertir un objet Python en data binaire, le second convertissant le binaire en ascii et le troisième étant une surcouche du second).
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  18. #18
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    Citation Envoyé par Sve@r Voir le message
    ca semble assez trivial comme manip. L'input clavier te donne une chaine, chaine envoyée dans le socket ([c]envoi = maSocket.send(bytes(requete + '\n', encodage))[/c) dans le client de Tyrtamos). Donc ici, concrètement, on envoie le contenu de la variable "requete". Ca devrait pas poser de souci d'y envoyer une autre variable, pourvu qu'elle contienne une chaine.

    Si maintenant tu veux passer autre chose (donc en fait on tombe là dans le binaire pur) alors soit tu envoies le binaire pur tel quel et à condition qu'il soit récupéré dans un réceptacle identique de l'autre côté ça fonctionnera ; soit tu convertis ton binaire en flux ascii via des outils comme pickle, bin2hex ou base64 (le premier pouvant convertir un objet Python en data binaire, le second convertissant le binaire en ascii et le troisième étant une surcouche du second).

    Bonjour,
    Non je veux uniquement passer du str.
    Mais en fait je passe par mes variables globale pour envoyer ou recevoir une information ce qui n'est pas très propre. Mais j'avoue que je ne sais pas trop comment envoyer ma ou recevoir mes données proprement via des variables.
    Merci pour ton aide.

  19. #19
    Expert éminent
    Avatar de tyrtamos
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    3 692
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 3 692
    Points : 7 165
    Points
    7 165
    Billets dans le blog
    6

    Par défaut

    Bonjour,

    Dans le code que j'ai proposé, le "client" envoie sa demande (qu'on appelle une "requête" sous forme de chaine) au serveur qui lui répond (une chaine) comme sait le faire un serveur. Regarde le code du client (mon message https://www.developpez.net/forums/d1.../#post10714846):

    - Etablissement d'une nouvelle connexion au serveur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
                # établit une nouvelle connexion
                maSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    maSocket.connect(adresse)
                except:
                    print("connexion impossible")
                    break # arrête le client
    - envoi de la demande au serveur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                # envoie la requête au serveur
                envoi = maSocket.send(bytes(requete + '\n', encodage))
    - réception de sa réponse:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
                # réceptionne la réponse du serveur
                reponse = str(maSocket.recv(buf), encodage).rstrip()
     
                # affiche la réponse
                print(reponse)
    Fermeture de la connexion ouverte:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
                # arrête la connexion
                maSocket.shutdown(socket.SHUT_RDWR)
                maSocket.close()
    En tout cas, au aucun cas le client va aller se servir lui-même dans les données du serveur, même si ses données sont des variables globales accessibles (cas du même PC)! Ou alors, ce n'est plus une solution "client-serveur".

    Cette solution "client-serveur" est très pratique puisque le client n'a rien à faire d'autre que de demander les infos qu'il veut, et c'est le serveur qui se débrouille pour lui donner...
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  20. #20
    Nouveau membre du Club
    Inscrit en
    septembre 2010
    Messages
    69
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 69
    Points : 35
    Points
    35

    Par défaut

    Je te remercie, je vais regarder ça.
    je t'avoue que mon plus gros problème c'est de pouvoir passer en paramètre une variable dans le client du socket. C'est pour ça que je passe par une variable globale.
    Je viens d'y passer la journée sans réel succès.
    Merci beaucoup pour ta contribution!
    je sais ce que je fais demain.
    Bonne soirée.

Discussions similaires

  1. [XL-2016] Variables partagées entre plusieurs fichiers
    Par Saperlipopette dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 08/11/2018, 15h58
  2. Variable globale entre plusieurs scripts
    Par Dan25 dans le forum VBScript
    Réponses: 2
    Dernier message: 01/03/2011, 17h15
  3. variable "globale" entre plusieurs fichiers
    Par kitue dans le forum Caml
    Réponses: 2
    Dernier message: 18/01/2010, 16h58
  4. Réponses: 12
    Dernier message: 14/08/2006, 12h55
  5. Partage de variables entre plusieurs pages
    Par bud_gw dans le forum Général JavaScript
    Réponses: 10
    Dernier message: 27/12/2005, 15h42

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