Forum Programmation.python client serveur par socket en clientThread

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
10
oct.
2016

Salut tous le monde,
Je suis en train de faire deux petits scripts python qui me font une copie d'un fichier de serveur (en mode daemon) vers un client qui passe par un socket.

Le serveur ressemble à ça:

fameuxsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fameuxsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
fameuxsocket.bind(("",1111))

while True:
    fameuxsocket.listen(10)
    print( "En écoute...")
    (clientsocket, (ip, port)) = fameuxsocket.accept()
    newthread = ClientThread(ip, port, clientsocket)
    newthread.start()

Le client a ça:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("", 1111))

print("Le fichier a récupérer:")
fichier01 = input(">> ") # utilisez raw_input() pour les anciennes versions python
s.send(fichier01.encode())
file_name = '/mon/path/pour/fichier %s' % (file_name,)
r = s.recv(1024)
with open(blaPremFich,'wb') as _file:
    _file.write(r)
print("Le fichier a été copié dans : %s." % file_name)

Et donc c'est la que le bas blesse! Car le fichier n'est pas copié


Agrandissement du membre par les suedois
Riche oncle africain fauché
passez votre chemin !

  • # J'aurais bien aimé t'aider ...

    Posté par  (site web personnel, Mastodon) . Évalué à 6.

    Mais ton code est partiel, avec des renommages de variables et/ou troncage de code, et le symptôme que tu remontes "pb le fichier n'est pas copié" est un symptome genre "et ça marche pas". Autant dire que ça manque d'infos pour nous aider à t'aider à résoudre ton problème.

    Si tu veux donner un peu plus de matériel et/ou de précision, ça pourrait bien être utile.

  • # boucler

    Posté par  . Évalué à 4.

    Tu dois lire sur la socket tant qu'elle est valide: tu pourrais très bien lire une chaîne vide, ça n'est pas pour autant qu'il n'y aura pas des données disponibles lors d'une prochaine lecture. Orthogonalement à ce problème, tu as aussi intérêt aussi à t'assurer que toutes les données ont été transférées, c.-à-d. en utilisant un protocole.

  • # Classe

    Posté par  . Évalué à -10.

    Tu peu faire une classe pour chaque connection et threader une instance à chaque connection, le code serait plus lisible, au niveau des performances je ne sais pas.

    Sinon tu peux utiliser les " exception "en python. C'est très utile cela permet de faire remonter les erreurs d'execution et de les gérer.

    Ils parlent ici d'un implémentation sur la lib socket :
    http://stackoverflow.com/questions/25447803/python-socket-connection-exception

    Toutes les erreurs que ton socket peut gérer sont décries dans la doc ici :
    https://docs.python.org/3/library/socket.html#socket.error

    ```exception socket.error

    A deprecated alias of OSError.
    
    Changed in version 3.3: Following PEP 3151, this class was made an alias of OSError.
    

    exception socket.herror

    A subclass of OSError, this exception is raised for address-related errors, i.e. for functions that use h_errno in the POSIX C API, including gethostbyname_ex() and gethostbyaddr(). The accompanying value is a pair (h_errno, string) representing an error returned by a library call. h_errno is a numeric value, while string represents the description of h_errno, as returned by the hstrerror() C function.
    
    Changed in version 3.3: This class was made a subclass of OSError.
    

    exception socket.gaierror

    A subclass of OSError, this exception is raised for address-related errors by getaddrinfo() and getnameinfo(). The accompanying value is a pair (error, string) representing an error returned by a library call. string represents the description of error, as returned by the gai_strerror() C function. The numeric error value will match one of the EAI_* constants defined in this module.
    
    Changed in version 3.3: This class was made a subclass of OSError.
    

    exception socket.timeout

    A subclass of OSError, this exception is raised when a timeout occurs on a socket which has had timeouts enabled via a prior call to settimeout() (or implicitly through setdefaulttimeout()). The accompanying value is a string whose value is currently always “timed out”.
    
    
    s'il exite une version Fr je ne suis pas au courant.
    
  • # TCP

    Posté par  . Évalué à 3.

    Bonjour,

    Le code que tu utilises ne marche pas pour une raison très simple : quand on fait de la communication réseau au niveau applicatif (comme toi), on utilise généralement une couche transport qui est UDP ou TCP. Tu as utilisé TCP (en précisant que la socket est de type SOCK_STREAM), et c'est une bonne chose pour ce genre d'utilisation, seulement, il faut comprendre comment TCP fonctionne.

    Quand tu demandes à TCP d'envoyer un message, il le découpe en plein de morceaux, chaque message est alors envoyé via IP (généralement), qui lui même va le filer à une couche lien, qui elle même va faire transiter le paquet sur un bon vieux câble (ou du wifi). Lors de la réception, l'inverse est effectué, en écoutant le câble, la couche lien va faire remonter le paquet IP, qui lui même revient à la couche TCP.

    Comme tu as pu le remarquer, d'un côté il y a un découpage implicite, de l'autre il n'y a pas de reconstruction, c'est une des particularités de TCP : il produit uniquement des flux. En conclusion :

    1. C'est à toi de délimiter la fin d'un message
    2. C'est à toi d'accumuler les bouts de message à la réception jusqu'à obtention d'un message complet

    Dans ton cas, TCP est particulièrement adapté car dès que tu lis un paquet, tu peux écrire directement ce morceau dans le fichier, et faire du streaming, plutôt que d'attendre d'avoir l'intégralité du fichier en mémoire avant de l'écrire. Note par exemple que le paramètre que tu donnes à recv est la taille maximale d'un message que tu veux lire … donc sauf si ton fichier fait moins de 1024 octets il te faudra nécessairement plusieurs recv pour récupérer ton fichier en entier.

    socket_client = ...
    
    while 1:
        message = socket_client.recv (1024)
        contenu,doit_terminer = fonction_qui_determine_si_on_est_a_la_fin (message)
        file.write (contenu)
        if doit_terminer:
            break

    J'espère que j'ai été assez clair et que tu peux maintenant continuer ton programme :-)

    • [^] # Re: TCP

      Posté par  . Évalué à -9.

      Et s'il n'arrive pas entiérement que se passera-t-il ? Si par exemple un routeur qu'il traverse prend feu !

      -Est ce qu'on aura une moitié de fichier à destination ou rien du tout ?
      -Est ce que tout les octects du fichiers seront à peu près en ordre ?
      -Tu parles du streaming mais ce n'est pas plutot UDP qui est utilisé pour ca ?

      Il y a peut-être un protocole plus sécure j'ai entendu parlé d'un truc qui est "l'encapsulation" : si TCP ne renvoit pas le fichier, on peut, peut-être avec l'encapsulation le forcer à afficher les données (incomplètes) qu'il envoit.
      Mais peut-être que cela va surcharger le transport innutilement ? (duplication des données ou des entêtes de prôtocole ?)

      Parsque peut-être que 1024 était choisis dans le but d'envoyer un message inférieur ou égal à 1024 il ne faut peut-être pas changer cette valeure.

      En tous cas j'éspère que le serveur de michelle mad fonctionne désormais.

      • [^] # Re: TCP

        Posté par  . Évalué à 1.

        Je ne suis pas expert en réseau, mais voilà comment il me semble que cela marche :

        1. La couche physique envoie des messages encodés d'une certaine manière et n'est pas fiable (ou peu). C'est le câble.
        2. La couche lien s'occupe de prendre une trame et de la convertir en un message encodé pour la couche physique. C'est le cas d'Ethernet, ou des normes WiFi. Encore une fois, même si elles font des choses pour limiter les pertes, elles ne sont pas fiables.
        3. La couche protocole (IP) s'occupe d'envoyer un message à une destination distance (c'est à dire pas uniquement sur le lien local) et gère en particulier le routage. Encore une fois elle n'est pas fiable (enfin, autant que les couches en dessous)
        4. Enfin, la couche transport (TCP/UDP) s'occupe des mécanismes plus haut niveau pour envoyer des messages via IP. En particulier, TCP va découper les messages, et même si les paquets arrivent dans le désordre il est capable de les remettre dans le bon. En plus, il gère dans une certaine mesure la perte de paquet et normalement il est fiable.

        C'est le protocole TCP qui gère tout pour nous dans le cas du streaming : l'ordre, la perte, etc …

        Ça dépend pour quoi, UDP est un protocole qui gère les choses de manière très différente. Par exemple, il y a une notion de message, et on récupère/envoie des messages « en un bloc ». En revanche, il n'y a aucune garantie d'ordre d'arrivée, et pas de gestion des pertes. En contre partie, c'est beaucoup plus rapide que TCP.

        De manière historique, TCP est clairement un protocole destiné à gérer des flux de données, et on peut le constater non seulement par la sémantique des appels recv et send, mais aussi en regardant le nom associé pour la création de la socket SOCK_STREAM.

        Cela dépend de l'utilisation, ici j'avais l'impression qu'il voulait quelque chose de simple, et ajouter encore une couche (comme HTTP) serait un peu surfait.

        Non il ne faut pas changer la valeur 1024, enfin, cela ne va pas changer grand chose. De toute manière, l'API socket est la suivante : quand on fait un recv (1024), il nous donne un contenu qui fera au plus 1024 octets, mais peut-être qu'il en fait beaucoup moins ! Du coup, même si la taille du fichier envoyé est inférieure à 1024, il se peut que TCP découpe et que deux/trois/mille recv soient nécessaires …

      • [^] # Re: TCP

        Posté par  . Évalué à 2. Dernière modification le 15 octobre 2016 à 00:08.

        -Est ce que tout les octects du fichiers seront à peu près en ordre ?

        TCP: pas à peu près mais complètement. Car chaque paquet est numéroté en séquence. Donc le receveur est en mesure de savoir ce qu'il lui manque, et il peut induire la retransmission de certains paquets qu'il n'aurait pas reçu. Ce qui est retourné par l'appel recv()/read() est garanti d'être dans l'ordre. Mais le receveur n'a aucun moyen de savoir si le receveur ne va pas encore envoyer des données. Si je te téléphone, ben je te dirais "au revoir" avant de raccrocher, tu sauras alors que je n'aurai plus rien à te dire. Par contre si je raccroche sans rien dire, tu ne saura pas si la communication a été coupée ou si j'ai effectivement raccroché. (note que TCP a théoriquement moyen de détecter la "fermeture" d'une connection, mais ça n'est pas très fiable car un firewall pourrait simuler la fermeture de la connexion, ton application n'y verrait que du feu).
        UDP: non

        -Est ce qu'on aura une moitié de fichier à destination ou rien du tout ?

        On aura ce qu'on aura reçu jusque là, modulo ce qui n'a pas encore pu être réassemblé (genre si on reçoit les paquets TCP 1 - 2 - 4 - 5, ben l'OS ne pourra au maximum ne retourner que 1 - 2 à l'application).

        -Tu parles du streaming mais ce n'est pas plutot UDP qui est utilisé pour ca ?

        Non, TCP == connecté (ou stateful) == on travaille avec un stream, une connection. Il est "découpé en paquet" de manière arbitraire par la stack tcp/ip et/ou les routeurs intermédiaires. On a la garantie que ce qui est read()/recv() le sera dans le même ordre que ce qui a été write()/send() de l'autre, mais on n'a pas de garantie quant à la taille des paquets tant en écriture qu'en lecture. La pile TCP a le droit de fusionner plusieur write en un seul paquet ou inversément de diviser un write en plusieurs paquets du côté émission. Similairement du côté réception, un paquet pourrait être réparti sur deux appels à read, ou encore plusieurs paquets pourraient être fusionnés. Cela permet d'augmenter les performance: par exemple en fusionnant des write() successifs de petite taille dans un seul paquet ou inversément d'attendre un certain temps après la réception d'un paquet pour voir s'il y en a un juste derrière dont les données pourraient être retournées lors du même appel à read()/recv().

        UDP == non-connecté (ou stateless) == l'unité de transfert est le datagramme. Dans ce cas, il me semble que la taille du buffer passé à recv/recvfrom doit être supérieure à la taille du plus gros datagramme recevable. En tout cas ce qui est certain c'est que l'os ne "réassemble" pas plusieurs datagrammes tel qu'il le ferait en TCP; car cela impliquerait l'utilisation d'un numéro de séquence attaché aux paquets afin d'éviter des le mélanger et rendrait de facto le protocole "stateful"… Cela veut dire qu'en pratique il faudra que le datagramme soit inférieur au plus petit MTU de la route utilisé (parce que sinon il va se retrouvé découpé en morceaux par le routeur et donc le récepteur n'aura plus aucun moyen de réassembler les fragments dans l'ordre, le paquet sera perdu).

        Comme Alumnium95 le mentionne, il y a des séparations entre les couches physiques, de protocoles (IP/TCP, IP/UDP) et applicatives (HTTP, DNS, etc.) Mon commentaire initial porte à confusion, je parlait de protocole dans la couche applicative évidemment. Pour éviter le cas de figure du routeur qui prend feu justement. Un protocole applicatif serait, p.e., d'écrire la chaine "ATTENTION J'ENVOIS UN FICHIER DE 42 OCTECTS" suivit du fichier en question, le récepteurs est donc au courant qu'il devra compter 42 octets à compter de la fin de cette chaîne reçue… En UDP, le protocole applicatif doit gérer la retransmission lui même (ou pas, si ça n'est pas utile). Un exemple de protocole serait "OCTET 24 VAUT 251" dans un sens, dans l'autre "JE N'AI PAS RECU L'OCTET 31, PRIERE DE LE REENVOYER". La confusion avec stream vient du fait qu'en générale, on n'utilise udp lorsque l'on n'a pas besoin de retransmission, par exemple lorsqu'on stream un flux audio en direct, ben si on a perdu une trame audio alors tant pis, ça n'aurait aucun sens de vouloir la diffuser plus tard (ça serait inaudible).

        • [^] # Re: TCP

          Posté par  . Évalué à 1.

          En UDP, le protocole applicatif doit gérer la retransmission lui même (ou pas, si ça n'est pas utile). Un exemple de protocole serait "OCTET 24 VAUT 251" dans un sens, dans l'autre "JE N'AI PAS RECU L'OCTET 31, PRIERE DE LE REENVOYER".

          Note pour plus de clarté: chaque chaine serait un datagramme indépendant, écrit séparément par un appel à sendmsg. Et reçue séparément par chaque recv.

        • [^] # Re: TCP

          Posté par  . Évalué à 1.

          Bon en y repensant et après vérif, il s'avère que la fragmentation est gérée au niveau IP, donc bon ça "pourrait" marcher d'avoir des gros paquets en UDP, mais c'est vivement non recommandé car il faut que le destinataire et/ou les routeurs ou nats intermédiaires fassent la reconstruction. Ça augment aussi fortement la probabilité de perdre un paquet.

          • [^] # Re: TCP

            Posté par  . Évalué à 1.

            La fragmentation au niveau du protocole IP c'est mal. En effet, si on fragmente un paquet IP, il n'y a pas de mécanisme de re-négociation, et donc il suffit de perdre un morceau du paquet pour se retrouver à renvoyer l'intégralité du paquet, ce qui n'est généralement pas une bonne idée.

            De plus, avec les NAT, ça devient marrant : « Reassembly is intended to happen in the receiving host but in practice it may be done by an intermediate router, for example, network address translation (NAT) may need to re-assemble fragments in order to translate data streams.[3] » (IP Fragmentation)

            Enfin, c'est vraiment pas très bien fait

            « In IPv4, hosts must make a best-effort attempt to reassemble fragmented IP datagrams with a total reassembled size of up to 576 bytes. They may also attempt to reassemble fragmented IP datagrams larger than 576 bytes, but they are also permitted to silently discard such larger datagrams. Applications are recommended to refrain from sending datagrams larger than 576 bytes unless they have prior knowledge that the remote host is capable of accepting or reassembling them »

            Du coup, bon, c'est bien gentil, mais autant passer par TCP, ou gérer son propre truc sur UDP, ce sera toujours mieux fait.

            • [^] # Re: TCP

              Posté par  . Évalué à 1.

              En "pratique" on peut aller raisonnablement jusqu'à une trame ethernet complète sans gros souci. Voir plus (4k) mais alors on conseil de faire une détection. Cf. RFC 3225 sur EDNS.

      • [^] # Re: TCP

        Posté par  . Évalué à 2.

        BTW ce sont des questions très pertinentes !

    • [^] # Re: TCP

      Posté par  . Évalué à 1.

      Très bien expliqué! maintenant je vois la raison de def une fonction qui gère la fin du message!
      Merci !

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.