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

C# Discussion :

Reception de message TCP difficile en mode Asynchrone


Sujet :

C#

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut Reception de message TCP difficile en mode Asynchrone
    Bonjour, j'ai un probleme avec un application TCP que j'essaie de faire.

    Elle envoie un message à un serveur, qui lui retourne des enregistrement de valeurs (séparés par des virgules). Le problème est que je ne peux prévoir la grandeur de ces enregistrement (souvent plus que 1000 enregistrements) donc je ne peux fixer mon buffer, ce qui m'oblige à faire plusieurs lecture.

    Voici le code tiré de la classe TcpSocket qui communique avec le serveur TCP

    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
    privatevoid WaitForData()
    {
    if (pfnCallBack == null)
    pfnCallBack = newAsyncCallback(OnDataReceived);
    // now start to listen for any data...
    m_asynResult = client.BeginReceive(m_DataBuffer, 0, m_DataBuffer.Length, SocketFlags.None, pfnCallBack, null);
    }
    publicvoid OnDataReceived(IAsyncResult asyn)
    {
    //end receive...
    int iRx = 0;
    iRx = client.EndReceive(asyn);
    char[] chars = newchar[iRx + 1];
    System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
    int charLen = d.GetChars(m_DataBuffer, 0, iRx, chars, 0);
    System.String szData = new System.String(chars);
    if (isNewData)
    {
    isNewData = false;
    receivedData = szData;
    }
    else
    {
    receivedData = receivedData + szData;
    }
    WaitForData();
    }
    
    
    // Fonction sendMessage
    // -- Sert a envoy‚ un message … l'h“te de la connection client
    // -- Re‡oit en parametre un message de type string
    // -- Envoie un erreur … la console si il y a un problŠme
    publicvoid sendMessage(String message)
    {
    try
    {
    message = message + "\r\n";
    byte[] byData = System.Text.Encoding.ASCII.GetBytes(message);
    isNewData = true;
    client.Send(byData);
    WaitForData();
    }
    catch (SocketException se)
    {
    MessageBox.Show(se.Message);
    }
    }
    publicstring getMessage()
    {
    string retour;
    if (receivedData != "")
    {
    retour = receivedData;
    }
    else
    {
    retour = "ChaŒne vide!";
    }
    return retour;
    }
    
    ce code est appelé par un bouton dans un formulaire extérieur de cette façon :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    unClient.sendMessage(message);
    reponse = unClient.getMessage();
    
    Il semble que la variable réponse soit rempli trop rapidement car la réponse attendu du serveur arrive après son affectation par "getMessage()"

    Y aurait-il un mécanisme que je pourrait utilisé pour différé son affectation, ou une technique qui m'échappe, car je suis un peu nouveau dans ce type d'opérations.

    Merci
    FadeOut

  2. #2
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Le problème à mon avis, c'est dans ton envoi de message

    La méthode WaitForData n'est pas bloquante (elle porte mal son nom donc), car tu effectues un appel asynchrone dans cette méthode. Résultat, ta méthode sendMessage rend la main avant que tu ais reçu toutes les données et c'est pourquoi ton getMessage ne te renvoie pas le résultat attendu.

    Il faudrait que tu fournisses un événement pour indiquer la fin de la réception (pour rester dans de l'asynchrone) afin de récupérer les données

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut
    Serait-il possible de rendre waitfordata bloquante??? Je suis un peu perdu dans ce genre d'opérations... Il semble que celles-ci s'apparentent au Thread.

    Si je voulais créer cet évenement?? de quelle façon devrais-je m'y prendre???


    Merci

  4. #4
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Tu peux rendre le tout bloquant oui, il suffit de ne pas utiliser les méthodes asynchrones pour la réception des données. Utilise la méthode Receive qui est bloquante

    En gros tu fais un appel à Receive et si tu as reçu des données tu boucles pour de nouveau faire un Receive jusqu'à ce tu ais reçu toutes les données.

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 760
    Par défaut
    synchrone ou pas, rien ne devrait empêcher le serveur d'expédier ses données en plusieurs messages et le client devrait pouvoir les lire avec une taille de buffer quelconque.
    Cela suppose un minimum de protocole pour convenir de ce que sont les unités d'information échangées.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut
    j'ai trouvé cette fonction sur internet, mais encore là, il faut présumé que nous savons le nombre de byte que nous allons recevoir...

    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
    privatestaticvoid Receive(Socket socket, byte[] buffer, int offset, int size, int timeout)
    {
    int startTickCount = Environment.TickCount;
    int received = 0; // how many bytes is already received
    do
    {
    if (Environment.TickCount > startTickCount + timeout)
    thrownewException("Timeout.");
    try
    {
    received += socket.Receive(buffer, offset + received, size - received, SocketFlags.None);
    }
    catch (SocketException ex)
    {
    if (ex.SocketErrorCode == SocketError.WouldBlock ||
    ex.SocketErrorCode == SocketError.IOPending ||
    ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
    {
    // socket buffer is probably empty, wait and try again
    Thread.Sleep(30);
    }
    else
    throw ex; // any serious error occurr
    }
    } while (received < size);
    }
    

    je cherche une façon de pouvoir contourner ce problème....

  7. #7
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Il n'y a pas à présumer du nombre d'octets que l'on va recevoir. Mes souvenirs de socket datent un peu mais je ne crois pas me tromper. Ca doit donner quelque chose dans ce style

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Définition du buffer de réception
    byte[] bytesReceived = new byte[1024];
    int bytes = 0;
    // Tant qu'il y a des données à lire
    do 
    {
        bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
        // Traitement du paquet reçu
        ...
    }
    while (bytes > 0);

  8. #8
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 760
    Par défaut
    Citation Envoyé par StormimOn Voir le message
    Il n'y a pas à présumer du nombre d'octets que l'on va recevoir. Mes souvenirs de socket datent un peu mais je ne crois pas me tromper. Ca doit donner quelque chose dans ce style:
    ...

    Tout à fait: un message de 1000 octets peut être lu en 10 lectures de 100.
    Encore faut-il savoir qu'il y aura 1000 octets à lire...
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #9
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Oui c'est vrai. L'idéal serait de faire un premier envoi de données, pour indiquer la taille des données qui vont être transmises. Simple de savoir si on a tout reçu par la suite.

    Par contre si la socket n'est plus nécessaire suite à l'envoi et qu'on la ferme, ça débloque la méthode Receive en renvoyant 0 il me semble. Cela correspondrait à l'exemple que j'ai indiqué.

    Après je ne suis pas un spécialiste des sockets et il y a probablement plus simple ^^

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut
    Malheureusement, je n'ai pas de contrôle sur le coté serveur de l'application tcp...


    Mais j'imagine, à moins d'être vraiment malchanceux, en lisant les packets par coup de 1024 bytes, et en comparant la longueur à 1024, si cette dernière est plus petite que 1024, on peut présumé la fin de la reception?

  11. #11
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Tu peux procéder ainsi, mais si manque de chance la taille des données que t'envoie le serveur est un multiple de la taille de ton buffer de réception, tu l'auras dans l'os

    Faudrait savoir comment l'application serveur gère les échanges.

  12. #12
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut
    Je suis finalement arrivé à une solution qui semble correct pour trouver la fin.

    En regardant les spécification du serveur TCP, il entoure les données d'un en-tête et d'une fin de message avec une syntaxe particulière en CMS

    donc... je continue de vérifié la longueur mais je fais un vérification sur la fin du "buffer"

    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
    privatestaticString Receive(Socket socket, int offset, int timeout)
    {
    int startTickCount = Environment.TickCount;
    byte[] buffer;
    char[] chars;
    String cmsdata = "";
    String tmpdata;
    String[] separatedData = newString[1000];
    int bufferSize = 1024;
    int received = 0; // how many bytes is already received
    int charLen;
    System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
    do
    {
    try
    {
    buffer = newbyte[1024];
    received = socket.Receive(buffer, 0, bufferSize, SocketFlags.None);
    chars = newchar[received + 1];
    charLen = d.GetChars(buffer, 0, received, chars, 0);
    tmpdata = new System.String(chars);
    cmsdata += tmpdata;
    separatedData = tmpdata.Split(',');
    }
    catch (SocketException ex)
    {
    if (ex.SocketErrorCode == SocketError.WouldBlock ||
    ex.SocketErrorCode == SocketError.IOPending ||
    ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
    {
    // socket buffer is probably empty, wait and try again
    Thread.Sleep(30);
    }
    else
    throw ex; // any serious error occurr
    }
    } while (received == bufferSize && separatedData[separatedData.Length - 2] != "END");
    return cmsdata;
    }
    
    malgré tout, j'ai l'impression que cette méthode est très "lourde" inutilement.

  13. #13
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Tu peux remplacer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    chars = newchar[received + 1];
    charLen = d.GetChars(buffer, 0, received, chars, 0);
    tmpdata = new System.String(chars);
    Par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tmpData = System.Text.Encoding.UTF8.GetString(buffer, 0, received);
    Sinon, ton buffer devrait être initialisé en dehors de la boucle, pas la peine de le recréer à chaque fois.

    La variable bufferSize ne sert à rien, puisque les objets tableau ont une propriété Length qui fait la même chose (utiliser buffer.Length à la place donc).

  14. #14
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 760
    Par défaut
    Il serait préférable de décrire comment est spécifié le serveur plutôt que de seulement poster le code.

    Le code alloue un buffer de 1024 dans lequel il va lire...
    On sort de la boucle si
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    received == bufferSize && separatedData[separatedData.Length - 2] != "END"
    Le "&&" me gène sauf s'il est spécifié que la longueur des messages est un multiple de 1024. L'autre question est de savoir ce qu'il se passe lorsqu'un message est > 1024 octets...

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

  15. #15
    Membre expérimenté
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Par défaut
    Je n'avais pas fait attention à la condition de sortie, mais effectivement le test de la taille des données reçues est inutile puisque dans les données quelque chose t'indique que tu as tout reçu. Autant ne pas multiplier les tests.

    L'autre question est de savoir ce qu'il se passe lorsqu'un message est > 1024 octets
    On lit par paquet de X octets jusqu'à ce que toutes les données en attente soient lues. Donc la taille du message transmis n'a aucune importance. Il me semble en tout cas.

  16. #16
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 760
    Par défaut
    Citation Envoyé par StormimOn Voir le message
    Je n'avais pas fait attention à la condition de sortie, mais effectivement le test de la taille des données reçues est inutile puisque dans les données quelque chose t'indique que tu as tout reçu. Autant ne pas multiplier les tests.

    L'autre question est de savoir ce qu'il se passe lorsqu'un message est > 1024 octets
    On lit par paquet de X octets jusqu'à ce que toutes les données en attente soient lues. Donc la taille du message transmis n'a aucune importance. Il me semble en tout cas.
    en supposant que la condition de sortie est la reconnaissance de la fin du message, tant que cette fin n'a pas été reconnue on boucle avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    do
    {
         try
        {
              buffer = new byte[1024];
    donc les effets de bords sont d'oublier les données déjà reçues (et de laisser le soin de faire le ménage à Mr Garbage Collector).

    Pour autant que la fin soit la chaine "END", le code me semble mal résister à un "END" à cheval sur 1024 - ex: N serait le 1024ième caractère.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  17. #17
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2006
    Messages : 74
    Par défaut
    en effet, la condition d'égalité est problématique...

    le problème est que parfois le serveur renvoie d'autres type de réponse (par exemple lorsqu'on se loggue sur l'application) qui ne fini pas par END

    je crois par contre que TOUT les réponse finissent par \0.... ce serait probablement la condition à vérifier....

    je vais tester cela...

    Merci à tous pour vos excellent conseil... cela va me permettre d'avancer énormément

  18. #18
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 760
    Par défaut
    Citation Envoyé par FadeOut Voir le message
    en effet, la condition d'égalité est problématique...

    le problème est que parfois le serveur renvoie d'autres type de réponse (par exemple lorsqu'on se loggue sur l'application) qui ne fini pas par END

    je crois par contre que TOUT les réponse finissent par \0.... ce serait probablement la condition à vérifier....

    je vais tester cela...

    Merci à tous pour vos excellent conseil... cela va me permettre d'avancer énormément
    Le "parfois" signifie qu'en fonction de l'état (et des commandes expédiées) le serveur va avoir un comportement spécifique. Il serait souhaitable d'expliciter un diagramme états/transitions pour que le client s'adapte aux différents cas.
    => ajouter une couche entre les traitements et les échanges de données pour que cela fonctionne comme attendu.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

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

Discussions similaires

  1. Réponses: 0
    Dernier message: 19/06/2015, 10h53
  2. envoi / reception de messages
    Par Snowflake dans le forum C
    Réponses: 4
    Dernier message: 05/01/2006, 14h13
  3. [.NET] Une question technique a propos du mode asynchrone
    Par nicknolt dans le forum Général Dotnet
    Réponses: 4
    Dernier message: 08/06/2004, 10h07
  4. [MFC]Pb de reception des messages!
    Par thief dans le forum MFC
    Réponses: 18
    Dernier message: 03/03/2004, 13h05
  5. Sockets + Receptions de messages
    Par raf_gug dans le forum MFC
    Réponses: 14
    Dernier message: 07/11/2003, 10h29

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