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

Réseau C Discussion :

Send() et Recv() non coordonnées


Sujet :

Réseau C

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut Send() et Recv() non coordonnées
    Bonjour,

    Je suis en train de développer un " serveur " d'envois multiples de fichiers. Ces derniers sont soit binaires, soit textes. Ce détail est important pour le client puisque en réception, je n'utilise pas les mêmes méthodes d'écritures dans mes fichiers.
    J'ai une boucle du côté client qui reçoit les messages spécifiant le type du fichier. Elle ne s'arrête seulement lorsque le nombre de fichiers effectivement reçus est égal au nombre de fichiers à recevoir.
    J'ai constaté que, si dans la boucle du côté client il y a plus d'instruction que dans celle du côté serveur, la fonction recv() reçoit plusieurs messages envoyés par send().

    Voici un exemple :
    Je dispose de quatre fichiers : txt, bin, bin, txt dans cet ordre précis.
    Si je fais simplement un envoi via send() côté serveur et une réception via recv() côté client, et que j'écris le contenu du buffer de réception dans un fichier à chaque tour de boucle, j'obtiens : txt, bin, bin, txt. En d'autres termes des messages corrects.
    Si je mets plus d'instructions dans ma boucle côté client, je ne reçois que deux messages : txt, binbintxt.
    J'ai utilisé des Sleep() côté serveur pour voir j'avais un problème de temps. Le résultat a été concluant, je reçois à nouveau txt, bin, bin, txt.

    Il doit y avoir une autre solution, plus rapide, non ?

  2. #2
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Par défaut
    Ce fonctionnement est normal (même s'il est perturbant).

    Pour 1 send(), il est parfois nécessaire d'avoir plusieurs recv()
    Pour plusieurs send(), il peut arriver qu'un seul recv() soit suffisant.

    Il ne doit pas exister dans ton code de lien entre le nombre de send() et le nombre de recv() et tu n'as pas le droit de dire 1 send() = 1 recv().

    Pour pallier ce problème, il y a deux techniques :
    • Soit l'émetteur envoie la longueur des données puis les données elles mêmes. Ainsi, le récepteur lit la longueur et sait combien il va recevoir de données ensuite. Cette technique est plutôt utilisée pour les protocoles orientés binaire (comprendre, qui envoie des données dont les valeurs des octets peuvent aller entre 0 et 255 sans aucune contraintes)
    • Soit les données sont délimitées/terminées par un caractère spécial (genre retour chariot ou code ascii 0). Cette technique est plutôt utilisée par les protocoles orientés texte (comprendre qui envoient du texte en clair, en ASCII)
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  3. #3
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par ram-0000 Voir le message
    Ce fonctionnement est normal (même s'il est perturbant).

    Pour 1 send(), il est parfois nécessaire d'avoir plusieurs recv()
    Pour plusieurs send(), il peut arriver qu'un seul recv() soit suffisant.
    Enfin... Normal en TCP/IP : si l'on passe en UDP/IP, on a bien du "un pour un"... Enfin, si le datagramme arrive à destination, du moins !!

    Ceci étant dit, vu que 99% des gens utilisent du TCP/IP pour leurs communications (et que c'est vraiment pas une mauvaise idée d'ailleurs), autant prendre l'habitude de ne pas penser en terme de "nombre" d'appels à send / recv, mais bel et bien en octets transférés.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  4. #4
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Par défaut
    Je parlais effectivement d'un protocole TCP

    Citation Envoyé par Mac LAK Voir le message
    Enfin... Normal en TCP/IP : si l'on passe en UDP/IP, on a bien du "un pour un"... Enfin, si le datagramme arrive à destination, du moins !!
    pour UDP, tu as bien résumé la situation !
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  5. #5
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par ram-0000 Voir le message
    pour UDP, tu as bien résumé la situation !
    Yep : "Pas de garantie de livraison en UDP, ni d'ordonnancement correct des trames à l'arrivée"...
    Alors que TCP garantit, lui, la livraison des données ainsi que l'ordre... Et "coûte" le fait que la réception n'est pas toujours synchrone avec l'émission en terme de nombre d'appels et/ou de taille des blocs reçus.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    C'est assez dommage. Cela me pose un problème d'algorithmie me semble t-il. En fait, voici ce se passe et ce qui devrait, par la suite, se passer :

    Une application, qui jouera le rôle du client, a pour but d'interpréter le contenu d'un fichier texte. Lorsque le contenu du document a été interprété, plusieurs fichiers sont générés : 0 à n fichiers binaires ET 0 à n fichiers textes.

    J'ai développé une application serveur qui elle fait ce travail là à partir du fichier que le client lui a envoyé. Les fichiers générés le sont donc dans un dossier dépendant du serveur. Une fois ce traitement effectué, le serveur doit renvoyer au client tous les fichiers générés. Je dois donc préciser plusieurs choses au client :

    - 1) Le nombre de fichiers qu'il doit recevoir.
    - 2) Pour chacun des fichiers générés, son nom, sa taille et s'il s'agît d'un fichier binaire ou texte.

    Côté client, je dois conditionner ma réception en fonction du type de fichier.
    Bref, je vais réfléchir à mon algorithme pour que cela fonctionne.

    Le problème c'est que si je dois envoyer la taille des données à transférer au client, je vais également être confronté à ce problème d'inéquivalence.

    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
    Tant Que (nbFichiersRecv < nbFichiersARecevoir)
              recevoir informations // Taille (octets) + Nom + type
              Si type = bin
                        nbOctetRecv <- 0
                        ouvrir fichier
                        Tant Que (nbOctetRecv < Taille)
                                  nbOctetRecv <- nbOctetRecv + recevoir données
                                  Ecrire données dans le fichier Nom
                        Fin Tant Que
                        fermer fichier
              Fin Si
    etc...
    
    Fin Tant Que
    Le problème c'est qu'au moment de la réception des informations, je ne recevrai pas correctement ce qui est envoyé.

  7. #7
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    C'est assez dommage. Cela me pose un problème d'algorithmie me semble t-il.
    C'est plutôt que tu as mal pensé, à l'origine, la communication entre les deux...

    Dans une communication, il faut toujours prendre en compte :
    • Le fait que les données peuvent ne jamais arriver.
    • Que l'un des points de communication (client, serveur, passerelle, ....) soit HS. Ceci peut aussi arriver en pleine transaction.
    • Toujours transmettre des données non-ambiguës : aucun élément sémantique de tes transactions ne doit pouvoir être confondu avec une partie d'un autre élément (principe de séparabilité des données transmises).

    Le plus simple pour arriver aux deux premiers, c'est de fonctionner en question / réponse (avec éventuellement rétablissement de la connexion), et toujours gérer les timeouts.
    Pour le deuxième, le principe moteur est : "la taille et le type des données ne doivent jamais être dissociés des données elles-même".

    Donc :
    • Soit le canal de communication lui-même définit le type de données (ex : canal de commande FTP), soit le type de la donnée doit être transmis.
    • Soit la taille d'une donnée est fixe en fonction de son type, soit il est variable. Dans ce cas, la taille (dans une unité adéquate) doit être transmise.


    Dans ton cas, tu peux par exemple considérer que chaque "commande" est une série de 3 octets, valant soit "txt", soit "bin"... Ou un code numérique équivalent.

    Citation Envoyé par theawe Voir le message
    Côté client, je dois conditionner ma réception en fonction du type de fichier.
    Heu... Pourquoi ? Un fichier texte peut aussi être transmis en mode binaire, sauf problèmes de codage des fins de ligne bien sûr et/ou d'encodage de caractères...

    Citation Envoyé par theawe Voir le message
    Le problème c'est que si je dois envoyer la taille des données à transférer au client, je vais également être confronté à ce problème d'inéquivalence.
    Et alors ? Tu as N octets à lire, ou P lignes, tu le sais avant de commencer à transférer, c'est tout... Je ne vois pas le problème, là.

    Citation Envoyé par theawe Voir le message
    Le problème c'est qu'au moment de la réception des informations, je ne recevrai pas correctement ce qui est envoyé.
    Si, si tu as correctement géré l'éventuel découpage des informations transmises.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Je me contente pour le moment de développer le scénario nominal...

    Le problème que je rencontre actuellement c'est que lors de la réception des informations du second fichier à récupérer (et je ne parle pas des données dans le fichiers, mais des données sur le fichier, comme dans mon post précédent), ces mêmes informations ne correspondent pas à ce qui est envoyé. A partir de ce moment là je ne peux pas, sauf erreur de ma part, envoyer un message au client, depuis le serveur, lui spécifiant la taille du fichier à recevoir.

    En fait je ne comprends pas. D'après moi il y a 2 (voire 3) réceptions à faire par tour de boucle, avec n tours de boucle et n = nombre de fichier à recevoir.
    Une fois dans la boucle je dois :

    1) recevoir les informations sur le fichier (concaténations du nom, type et taille)
    2) recevoir les informations contenu dans le fichier par paquets en vérifiant que la taille des données reçues est bien égale à la taille des données envoyées.

    Sauf incompréhension totale de ma part, cela me donne une équivalence entre send() et recv().
    Ce que je parviens pas à faire c'est récupérer les données des fichiers envoyés au n-ième envoi avec n > 1.

    Je ne comprends clairement pas l'inéquivalence dans la pratique. Si je veux envoyer un message, j'appelle send(). Si je veux le recevoir, j'appelle recv(). Et, même si je boucle en fonction du nombre d'octets à recevoir, j'ai toujours cette équivalence dans ma boucle.

    Le problème rencontré dans mon premier post a juste soulevé ceci d'après moi : un send() peut-être envoyé à tout moment. Je peux en envoyer trois à la suite, par exemple, et les recevoir avec un seul recv() si l'appel de recv() est ultérieur à celui du 3ème send(), non ?

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    Je peux en envoyer trois à la suite, par exemple, et les recevoir avec un seul recv() si l'appel de recv() est ultérieur à celui du 3ème send(), non ?
    C'est très exactement ça. En fait, cela dépend de plein de paramètres, notamment et en vrac :
    • Taille de buffer logiciel de l'émetteur.
    • Taille du buffer du contrôleur Ethernet de l'émetteur.
    • MTU du réseau traversé, et des passerelles.
    • Taille du buffer du contrôleur Ethernet du récepteur.
    • Taille de buffer logiciel du récepteur.
    • Chronologie des appels recv() et send().
    • Latence de la pile TCP/IP pour chaque système.
    • Charge du réseau.
    • Et j'en oublie des dizaines d'autres...


    Tes trois send(), tu peux les recevoir aussi bien avec un seul recv(), que deux, ou même N recv() en lisant un seul octet à chaque fois. Tu ne peux JAMAIS présumer de la taille du bloc lu via recv().

    Si tu as un problème avec ça, c'est que tu "découpes" mal tes envois initiaux... Par exemple, pas de taille indiquée, pas de retour à la ligne pour des commandes "texte", etc.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    En fait, c'est plutôt que j'utilise mal la fonction recv(). Si j'ai bien compris, en gros si j'envoie depuis le serveur "bin" ou "txt" il va falloir que je boucle ma réception jusqu'à ce que j'ai reçu 3 octets... ?

  11. #11
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    En fait, c'est plutôt que j'utilise mal la fonction recv(). Si j'ai bien compris, en gros si j'envoie depuis le serveur "bin" ou "txt" il va falloir que je boucle ma réception jusqu'à ce que j'ai reçu 3 octets... ?
    C'est exactement ça. Ou, pour être plus précis, lire ce qu'il y a, le mettre dans un buffer interne au besoin (si c'est volumineux, en général), et lire ensuite la prochaine "commande", trois octets dans ton cas, pour savoir quoi faire des données qui suivent.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  12. #12
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Bonjour,

    Aujourd'hui je suis complètement affligé (!) par les Sockets. Je ne vois pas comment poser l'algorithme de mes différentes boucles de réceptions. J'ai tenté des choses mais à chaque fois, je n'avais aucun contrôle de ce qui était reçu par un appel à recv(). Si je dois recevoir 3 octets, je vais, comme dit plus tôt, devoir boucler ma réception en fonction de cette donnée. Seulement si je ne contrôle pas ce qui est reçu par recv() en termes de taille de données reçues, comment puis-je m'assurer de la bonne réception de mes 3 octets ?

    Un autre problème me vient à l'esprit : Si je ne peux pas envoyer mes données au coup par coup, comment puis-je recevoir correctement la taille de mes données, pour ensuite boucler en fonction de celle-ci ?

    Physiquement cela me paraît simple :

    Dans une boucle côté Serveur j'envoie :
    1) La taille du fichier
    2) Le nom du fichier
    3) son type
    4) son contenu
    Ce, autant de fois qu'il y a de fichiers à envoyer.

    Dans une boucle côté Client je reçois :
    1) La taille du fichier
    2) Le nom du fichier
    3) son type
    4) son contenu
    Ce, autant de fois qu'il y a de fichiers à recevoir.

    J'en suis pour le moment aux étapes 2) et 3) de chaque côté. Je reçois donc la taille par équivalence, au coup par coup...
    Je reçois ensuite la concaténation des 2) et 3) au coup par coup également. Mon problème se situe pile ici. Si je n'avais voulu envoyé que le nom du fichier et ceci non pas par équivalence mais en fonction de la taille de ma chaîne de caractères, il aurait fallu que je l'envoie en amont, non ?
    Si oui, j'aurais été dans l'incapacité de la réceptionner parfaitement, étant donné que je me serais retrouvé dans une équivalence.

    J'ai l'impression de tourner en rond, d'autant plus que là, j'avais finalement fini par trouver un moyen d'envoyer les informations dont j'avais besoin ( 1) 2) 3) ). Moyen qui, un peu plus tard, a dû être abandonné en raison de conflits pour le moins étranges. Ces conflits se sont produits lors du transfert des informations 1) 2) 3), débuté juste après la première génération des fichiers côté serveur. Un fichier généré était oublié (j'en cherche encore la raison puisque toutes les vérifications faîtes (:MessageBox, écriture dans un fichier) faisaient bien apparaître ce fichier). Pour les fichiers qui avaient été effectivement biens pris en compte, je ne récupérais que la taille du premier fichier pour chacun de mes fichiers, et la taille de tous mes fichiers en lieu et place de leurs noms respectifs.

    Auriez-vous des idées ? des questions ?

  13. #13
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Je pense avoir identifié le problème par rapport à la mauvaise réception des informations sur les fichiers générés pour la première fois. En fait, plusieurs fichiers ayant une taille de 0 sont générés pour un exemple précis d'exécution. En revanche lorsque je ne génère pas de fichiers vides, toutes mes données sont bien acheminées. C'est cette différence qui m'a conduit sur l'hypothèse énoncée précédemment.

    En général, lorsque je dois récupérer un entier, que ce soit sur le serveur ou chez le client, j'ai pris l'habitude de le convertir en const char *. Une fois la données reçue, je la reconvertis en entier. J'ai tenté de remédier à mon problème en ajoutant des conditions dans le code. Par exemple, si la taille du fichier à envoyer est égale à 0, au lieu d'envoyer 0, j'ai envoyé une chaîne de caractères spécifique, mais le résultat n'a pas été concluant. Je suis toujours confronté aux mêmes difficultés au niveau de la réception.

    Un petit exemple pour que ce soit plus clair :

    Imaginons que pour un fichier f le serveur génère 4 fichiers.
    J'obtiens ceci, après écriture dans un fichier :

    fichier généré 1 : taille = tailleFichier1 | nom = nomFichier1
    fichier généré 2 : taille = tailleFichier2 | nom = nomFichier2
    fichier généré 3 : taille = tailleFichier3 | nom = nomFichier3
    fichier généré 4 : taille = tailleFichier4 | nom = nomFichier4

    Résultat : toutes les données sont reçues.

    Autre exemple (problème) :
    un fichier f génère 7 fichiers dont un ou plusieurs fichiers ont une taille égale à 0.

    J'obtiens ceci, après écriture dans un fichier :
    fichier généré 1 : taille = 0 | nom = tailleFichier2
    fichier généré 1 : taille = 0 | nom = tailleFichier3
    fichier généré 1 : taille = 0 | nom = tailleFichier4
    fichier généré 1 : taille = 0 | nom = tailleFichier5
    fichier généré 1 : taille = 0 | nom = tailleFichier6
    fichier généré 1 : taille = 0 | nom = tailleFichier7

    Les données du fichier 1 ont disparu (qui a une taille non nulle). Les noms des fichiers n'ont pas été reçus par le client. C'est assez étrange et je ne me l'explique pas. J'ai cherché ci et là, mais je ne trouve pas de solution.
    Biensûr, je souhaite pas vraiment transférer un fichier dont la taille est égale à 0. J'aimerais juste pouvoir le créer côté client. J'ai donc pour cela besoin de récupérer ces informations.

  14. #14
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Bonjour...

    Aujourd'hui, rien n'a évolué en mon sens. Voici la synthèse des problèmes rencontrés :

    1) En TCP/IP, il n'y a pas équivalence entre un envoi (un appel à send()) et une réception (un appel à recv()). Il faut donc prendre en compte plusieurs paramètres, parmi lesquels ceux qui ont été cités plus haut sur le fil.

    Il faut en règle générale envoyer la taille de ce qui doit être envoyé, pour pouvoir boucler sur cette donnée la réception.
    Cela pose un soucis majeur :

    Comment recevoir la taille si, la simple réception d'une chaîne de caractères, à un moment donné du processus, est impossible ?

    2) J'ai tenté d'envoyer à un moment donné, une taille entière, convertie par la suite en chaîne de caractères (avec sprintf()) et dont la valeur était "0". Problème de calcul de fin de chaîne de caractères ? Pour y remédier j'ai conditionné mon envoi de la taille. Si elle était égale à 0, j'envoyais une chaîne de caractères autre (n'importe laquelle). Sinon, j'envoyais la taille réelle.
    Ce que je pensais être une astuce n'en est pas une en fait, car cela n'a pas résolu le problème. Ce que je n'ai d'ailleurs pas saisi pour l'instant...

    J'aurais souhaité pouvoir déboguer tout ceci simplement, mais je travaille avec deux applications MFC Visual C++, ce qui rend le pas à pas laborieux, d'autant plus que mes appels à recv() sont bloquants...

    Voici, pour aller plus loin dans l'explication des applications, ce qui devrait se passer :


    Côté client :

    • Sélection du fichier à exécuter sur le serveur.
    • Envoi des propriétés du fichier (taille et nom).
    • Envoi du contenu du fichier par séquences.


    Côté serveur :

    • Réception des propriétés du fichier à exécuter.
    • Création ou ouverture du fichier.
    • Ecriture du contenu du fichier par séquences.
    • Détermination de l'identité des fichiers générés (ces derniers seront envoyés automatiquement au client) avec incrémentation d'un compteur.
    • Envoi du compteur au client.


    Côté client :

    Réception du nombre de fichiers à recevoir.

    Côté serveur :

    • Pour chacun des fichiers à envoyer :
      1. Récupération des propriétés (taille, nom et type).
      2. Envoi de la taille.
      3. Envoi du nom + type.
      4. Envoi séquentiel du fichier.
    • Fin Pour


    Côté client :

    • Pour chacun des fichiers à recevoir :
      1. Réception de la taille.
      2. Réception du nom + type.
      3. Réception séquentielle du fichier et écriture dans le fichier nom.
    • Fin Pour


    Parce qu'il est difficile de répondre sans le code sous les yeux, qu'il y a tant de questions qui fusent, je préfère laisser ce sujet en suspens. Je regrette de n'avoir pu trouver ici et là, un exemple d'envois simultanés, pour m'en imprégner.

    Mes connaissances, tant en réseau qu'en langage C/C++, sont trop peu étendues, je ne peux suivre, je passe.

    Si jamais quelqu'un avait la solution à mon problème et l'apportait ici, il serait lu.

  15. #15
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    Comment recevoir la taille si, la simple réception d'une chaîne de caractères, à un moment donné du processus, est impossible ?
    En général, on envoie la taille sous forme binaire, c'est à dire un entier 32 bits au format réseau (Big-endian), que l'on convertit avec les fonctions htonl et ntohl afin d'assurer l'indépendance de la plate-forme.

    Pour être plus précis, on utilise son propre protocole, c'est à dire :
    • Un entête de taille connue décrivant la nature et la taille de la prochaine donnée prévue.
    • Les données en question.


    Ainsi, ton programme boucle sur la réception d'un entête (ex : 20 octets), et ne fait RIEN tant qu'il n'a pas reçu ça. Une fois l'entête reçu et "décodé", ton programme sait alors quoi faire, et quelle taille ont les données à recevoir (y compris de taille zéro si nécessaire). S'il reste des données dans la socket, c'est forcément... un nouvel entête, donc ne lire que les N octets correspondants, et refaire la manip.

    Citation Envoyé par theawe Voir le message
    Ce que je pensais être une astuce n'en est pas une en fait, car cela n'a pas résolu le problème. Ce que je n'ai d'ailleurs pas saisi pour l'instant...
    Envoyer des données sous forme de texte n'est pas forcément le plus simple avec les sockets...

    Citation Envoyé par theawe Voir le message
    Si jamais quelqu'un avait la solution à mon problème et l'apportait ici, il serait lu.
    La solution est (relativement) simple, après tout dépend de ce que tu veux vraiment : veux-tu le faire toi-même, donc apprendre la communication réseau, ou juste assurer la fonction ?

    Si c'est pour apprendre, n'importe quel source de programme assurant une communication réseau quelconque (FTP, chat, navigateur web, etc.) t'aidera.
    Si c'est pour assurer la fonction, piloter le programme "TFTP.exe" devrait résoudre ton problème (Tiny File Transfert Protocol).
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  16. #16
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Pour être plus précis, on utilise son propre protocole, c'est à dire :
    • Un entête de taille connue décrivant la nature et la taille de la prochaine donnée prévue.
    • Les données en question.
    Je ne contrôle pas le nom des fichiers, ni leur taille. Je ne peux pas savoir à l'avance quel nom, et donc quelle taille aura cette donnée, auront les fichiers à envoyer. En d'autres termes, je ne peux pas connaître (sauf erreur de ma part) la taille d'un tel entête.

    Citation Envoyé par Mac LAK Voir le message
    Ainsi, ton programme boucle sur la réception d'un entête (ex : 20 octets), et ne fait RIEN tant qu'il n'a pas reçu ça. Une fois l'entête reçu et "décodé", ton programme sait alors quoi faire, et quelle taille ont les données à recevoir (y compris de taille zéro si nécessaire). S'il reste des données dans la socket, c'est forcément... un nouvel entête, donc ne lire que les N octets correspondants, et refaire la manip.
    Une fois l'entête reçu, dois-je quitter la boucle pour traiter ("décoder") mes données et en recevoir ?
    Comment vérifier qu'il reste des données dans la socket ?

    Citation Envoyé par Mac LAK Voir le message
    La solution est (relativement) simple, après tout dépend de ce que tu veux vraiment : veux-tu le faire toi-même, donc apprendre la communication réseau, ou juste assurer la fonction ?

    Si c'est pour apprendre, n'importe quel source de programme assurant une communication réseau quelconque (FTP, chat, navigateur web, etc.) t'aidera.
    Si c'est pour assurer la fonction, piloter le programme "TFTP.exe" devrait résoudre ton problème (Tiny File Transfert Protocol).
    L'idéal serait dans un premier temps d'apprendre la communication réseau pour ensuite assurer au mieux la fonction, non ?

    J'ai déjà vu la source d'un chat et j'imagine celle d'un FTP. Il n'y aurait pas de problèmes si je devais envoyer des fichiers un par un...
    Là, je suis dans un cas où je dois en envoyer n, avec n très variable. Le temps que je traite les données sur le fichier à partir du client, j'ai envoyé depuis le serveur bien trop d'informations. Je dois pourtant faire en sorte d'envoyer tous les fichiers le plus vite possible.

    Pour reprendre la proposition de code implicitement faite plus haut, je pense qu'il y aura quelques problèmes, par rapport à ce que je sais faire :

    Imaginons que j'envoie mon entête correctement (le plus simple à vrai dire...), comment vais-je la récupérer ? avec un recv() je suppose, non ? Et dans un buffer de la taille de mon entête ou en utilisant sizeof(unBuffer) ?
    Après, je décode correctement mes données et débute le transfert d'un fichier, ok.
    Comment puis-je faire en sorte que, lors de l'écriture des données dans le fichier, je n'écrive pas plus que je ne reçois ?
    Parce que cela serait simple si, une fois les données contenues dans le fichier envoyées, je n'avais plus rien à recevoir. Dans mon cas, j'ai d'autres données à recevoir. En d'autres termes, si je n'applique pas une équivalence send()/recv(), je ne reçois rien correctement.
    Autre question : En supposant que les étapes précédentes aient été correctement effectuées, s'il reste des données dans ma socket, comment je peux m'assurer que ces données soient effectivement celles d'un entête ?
    Puisqu'entre temps, j'ai déjà lancé plusieurs send() depuis le serveur...

    Je pense que le problème se résout à cette question : Comment imposer à recv() de ne recevoir que n octets ? mais peut-être que je me trompe totalement donc...

    Bon, je vais réfléchir à l'algorithme à mettre en place et vous le propose dès que c'est fait.

  17. #17
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    Je ne contrôle pas le nom des fichiers, ni leur taille. Je ne peux pas savoir à l'avance quel nom, et donc quelle taille aura cette donnée, auront les fichiers à envoyer. En d'autres termes, je ne peux pas connaître (sauf erreur de ma part) la taille d'un tel entête.
    Tu fais erreur, justement, dans le sens où tu n'as pas assez compris le principe d'entête.

    Exemple, j'omets sciemment les send/recv pour ne mettre que les valeurs. "ORDRE" signifie que c'est un entête, le chiffre entre parenthèses la taille associée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    ORDRE(16) : Transfert de fichier, envoi du nom.
    NOM DE FICHIER : NomDuFichier.txt
    ORDRE(4) : Transfert de fichier, envoi de la taille.
    TAILLE DE FICHIER : 46546213 [octets].
    ORDRE(46546213) : Transfert de fichier, contenu.
    CONTENU DU FICHIER : ...........................
    Tu comprends mieux ?

    Citation Envoyé par theawe Voir le message
    Une fois l'entête reçu, dois-je quitter la boucle pour traiter ("décoder") mes données et en recevoir ?
    Un entête (aussi parfois appelé "token" ou "instruction") = un traitement, en général dans un automate a états finis d'ailleurs.

    Citation Envoyé par theawe Voir le message
    Imaginons que j'envoie mon entête correctement (le plus simple à vrai dire...), comment vais-je la récupérer ? avec un recv() je suppose, non ? Et dans un buffer de la taille de mon entête ou en utilisant sizeof(unBuffer) ?
    Bien sûr. Tu le récupères exactement comme tu le ferais depuis un fichier sur le disque, sauf que tu utilises recv() à la place de read() !

    Citation Envoyé par theawe Voir le message
    Comment puis-je faire en sorte que, lors de l'écriture des données dans le fichier, je n'écrive pas plus que je ne reçois ?
    Comment ça ? Tu connais les tailles à l'avance, donc tu ne lis JAMAIS plus que ce qui est attendu, tout simplement.

    Citation Envoyé par theawe Voir le message
    En d'autres termes, si je n'applique pas une équivalence send()/recv(), je ne reçois rien correctement.
    Si, en utilisant les tailles et des tokens (entêtes) pour "séparer" les données et dire, surtout, ce que sont ces données.
    En gros, ça passe par l'envoi d'une valeur d'énumération (le "code" qui dit ce que l'on va faire), et la taille de la prochaine donnée (éventuellement zéro s'il n'y a pas de données à recevoir).
    Donc, pour transférer un fichier, tu annonces un transfert de fichier, tu envoie le nom, puis la taille du fichier, puis les données.

    En général, pour simplifier un peu tout ça et rendre le programme plus fluide, on mets un thread en lecture sur la socket, et on le laisse vider la socket au maximum possible. Il remplit ensuite un buffer interne à ton programme, dont tu peux contrôler la taille totale, savoir combien d'octets sont dedans, etc. Mais on peut aussi se débrouiller sans et faire avec les buffers internes de la pile TCP/IP, le plus souvent amplement suffisants.

    Citation Envoyé par theawe Voir le message
    comment je peux m'assurer que ces données soient effectivement celles d'un entête ?
    Tu ne le peux pas, sauf par protocole. C'est ton protocole qui dit "un entête + des données" à chaque envoi, éventuellement les données peuvent être absentes mais dans ce cas l'entête le précise.
    Donc, après avoir "fini" un traitement unitaire, c'est toujours un entête qui suit.

    Citation Envoyé par theawe Voir le message
    Puisqu'entre temps, j'ai déjà lancé plusieurs send() depuis le serveur...
    En TCP/IP, les données sont garanties livrées et ordonnées. Elles ne seront jamais mélangées à l'arrivée, et arrivent dans l'ordre exact de l'envoi.
    Tu ne peux pas te fier au NOMBRE de send / recv, certes, mais les données elles-mêmes sont garanties dans le bon ordre.

    Citation Envoyé par theawe Voir le message
    Je pense que le problème se résout à cette question : Comment imposer à recv() de ne recevoir que n octets ? mais peut-être que je me trompe totalement donc...
    Tu mets le "n" en question en 3ème paramètre, pour lui faire "croire", donc, que ton buffer est limité à cette taille. La fonction ne remplira alors pas plus que ces n octets.

    Ta fonction de lecture d'entête est donc "recv(s,&entete,sizeof(entete),MSG_WAITALL);". Le flag est pour être certain de ne pas revenir avant d'avoir effectivement la totalité de l'entête.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  18. #18
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Je viens à l'instant de saisir quelque chose. Encore quelques questions et je pense que ce sera résolu.

    Finalement, je peux envoyer autant de données que je le souhaite via le serveur et ces données ne seront pas perdues. Je pourrai les récupérer à tout moment, dans l'ordre de l'envoi. Récupérer comme un message contenant toutes mes données, que je vais découper en réception, en fonction de la taille de chacune d'elles et ce, en lisant le message de gauche à droite. C'est ça ?

    En fait, c'est comme si je concaténais toutes mes données ensemble (taille + nom + contenu), que j'envoyais tout d'un coup, que je recevais tout d'un coup et que je découpais mon buffer en fonction de ce que je voudrais récupérer, mais en bien plus propre (plus de contrôle des données envoyées etc.), plus rapide ?

    Un peu comme dans un chat où le client enverrait au serveur un message ayant pour première lettre "c" pour une connexion, "d" pour une déconnexion, "m" pour un message de chat ?

  19. #19
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par theawe Voir le message
    Finalement, je peux envoyer autant de données que je le souhaite via le serveur et ces données ne seront pas perdues.
    En effet, c'est d'ailleurs TCP/IP qui te le garantit. Si besoin, le serveur cessera d'envoyer des données si c'est trop "plein" de ton côté.

    Citation Envoyé par theawe Voir le message
    Je pourrai les récupérer à tout moment, dans l'ordre de l'envoi.
    Tout à fait. Attention donc au multithreading, il ne faut pas laisser plusieurs threads taper dans la socket en même temps sans contrôler tout ça.

    Citation Envoyé par theawe Voir le message
    Récupérer comme un message contenant toutes mes données, que je vais découper en réception, en fonction de la taille de chacune d'elles et ce, en lisant le message de gauche à droite. C'est ça ?
    Tout à fait. Enfin, pour du mode texte, en binaire c'est encore plus simple. Je te conseille d'ailleurs de faire tes envois de façon binaire plutôt que texte, c'est bien plus simple à gérer pour un débutant je trouve.

    Citation Envoyé par theawe Voir le message
    En fait, c'est comme si je concaténais toutes mes données ensemble (taille + nom + contenu), que j'envoyais tout d'un coup, que je recevais tout d'un coup
    Presque : tu n'es pas certain de tout recevoir d'un coup, justement. Aucune corrélation n'est possible entre le nombre de send et le nombre de recv : tu peux avoir besoin de plus de recv que de send, le contraire, etc.
    Bref, n'importe quoi. La seule garantie, c'est que SI tu as les données, elles sont bonnes, dans le bon ordre et tu n'auras pas de duplication non voulue.
    Après, on n'est jamais à l'abri d'une coupure réseau ou d'un plantage : les données peuvent donc ne jamais arriver, il faut toujours prévoir le cas.

    Citation Envoyé par theawe Voir le message
    Un peu comme dans un chat où le client enverrait au serveur un message ayant pour première lettre "c" pour une connexion, "d" pour une déconnexion, "m" pour un message de chat ?
    C'est le principe d'entête (ou de "token"), justement.


    Tu vois bien que c'est simple, non ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  20. #20
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    96
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 96
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Tu vois bien que c'est simple, non ?
    J'en aurais même presque honte .

    Trois dernières choses :

    1) Si jamais je garde la méthode des chaînes de caractères, je ne vois pas comment préciser la taille de la donnée dans l'entête, pour pouvoir ne récupérer que cette taille côté client.
    En me basant sur ton exemple :

    ORDRE(16) : Transfert de fichier, envoi du nom.
    NOM DE FICHIER : NomDuFichier.txt

    Je ne vois pas comment le client récupère la valeur 16. Mais je n'ai pas vraiment encore bien poussé ma réflexion, je pensais à ceci :

    2) Est-ce possible de déclarer 1 structure pour chaque information ?
    Comme ceci par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    typedef struct
    {
              int iTailleNomFichier; //ou long
              CString nomFichier; //ou char[n] ou autre...
    } 
    enteteNomFichier;
    Ou un truc qui y ressemble ? Je risque d'avoir des problèmes au niveau de la taille de enteteNomFichier si le nom varie je suppose...

    3) Qu'entends-tu par "envois de façon binaire" ? envoi de types différents de tous ceux relatifs aux chaînes de caractères ?

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Sur un socket : send et recv ou read et write ?
    Par Médinoc dans le forum Réseau
    Réponses: 35
    Dernier message: 05/11/2009, 16h51
  2. [socket] Pb send() et recv()
    Par Tymk dans le forum C++
    Réponses: 6
    Dernier message: 03/06/2008, 19h44
  3. pb avec send et recv de mpi
    Par fatjoe dans le forum C++
    Réponses: 0
    Dernier message: 24/02/2008, 22h54
  4. socket send et recv
    Par sebatlante dans le forum Réseau
    Réponses: 24
    Dernier message: 29/08/2007, 02h34
  5. Question sur les fonctions "send()" et "recv(
    Par damien99 dans le forum MFC
    Réponses: 6
    Dernier message: 10/02/2006, 21h47

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