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 :

[Winsock] Parcourir la pile de réception des messages


Sujet :

Réseau C

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut [Winsock] Parcourir la pile de réception des messages
    Bonjour,

    Je programme actuellement une bibliothèque permettant l'utilisation des protocoles TCP et UDP à l'aide de Winsock.
    Cette bibliothèque permet de gérer un client simple mais aussi de gérer un serveur multi-clients.
    Avec le protocole UDP, le serveur, pour simuler une connexion distante avec les clients, va conserver les informations reçues grâce à la fonction recvfrom() et la structure sockaddr_in.
    Ces informations vont permettre au serveur de pouvoir envoyer une donnée/d'en recevoir d'un client spécifique et aussi permettre de vérifier qu'un client ne se connecte pas deux fois au serveur.

    Pour recevoir des données en provenance du bon client, j'utilise ce code:

    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
     
    sockaddr_in buffer_sockaddr;
    TIMECAPS caps;
    ZeroMemory(&caps,sizeof(caps));
    timeGetDevCaps(&caps,sizeof(caps));
    int size_sin = sizeof(sockaddr_in);
     
    do
    {
     ZeroMemory(&buffer_sockaddr,sizeof(buffer_sockaddr));
     Sleep(caps.wPeriodMin);
     
     //Récupération du sockaddr_in du client sans modifier la pile de réception
     recvfrom(m_sock,NULL,0,MSG_PEEK,(SOCKADDR*)&buffer_sockaddr,&size_sin); 
     
     
    } while ( /* test de la bonne IP du client ainsi que du bon port de connexion */)
     
    //Si on arrive ici, c'est que le message provient du bon client donc on le récupère
    recvfrom(m_sock,buffer_data,size_data,0,(SOCKADDR*)&m_client[id].sin,&size_sin);
    Dans ce code, m_sock correspond à la socket du serveur et m_client[id] correspond au client ciblé.

    Malheureusement, ce code ne permet que de récupérer le premier message de la pile donc si le message du client ciblé se situe après le premier message, il ne sera jamais récupéré.
    Ma question est donc: Comment peut-on parcourir la pile de réception afin de récupérer le message situé telle ou telle position ?

    Merci d'avance pour vos réponses.

  2. #2
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    Petit up car je désespère toujours sur ce problème...

    Peut-être que je me suis mal exprimé dans mon premier message alors je vais tenter de reformuler :

    J'ai créé une bibliothèque permettant la création d'un serveur UDP qui peut gérer plusieurs clients à la fois.
    Chaque utilisateur qui se "connecte" au serveur UDP est enregistré dans un tableau de sockaddr_in afin de pouvoir le reconnaître par la suite.
    Pour que le serveur puisse gérer plusieurs clients, je voudrais qu'il puisse récupérer le message d'un client précis (par exemple le client n°4), comme le fait la fonction "recv" pour le protocole TCP qui se sert de la socket client.
    Cependant, la fonction "recvfrom" ne permet pas de sélectionner un message d'un destinataire précis car elle ne fait que recevoir le tout premier message qui arrive au serveur.
    Pour l'instant, j'ai fait un code qui récupère le premier message reçu (grâce à la fonction "recvfrom" avec comme argument MSG_PEEK afin de ne pas modifier la file de réception) et qui compare ensuite la structure sockaddr_in récupérée avec la structure sockaddr_in du client dont on veut recevoir un message.
    Si les deux structures sont identiques, alors le message provient du bon destinataire et dans ce cas on peut récupérer le message sans l'argument MSG_PEEK.
    Par contre, si les structures ne sont pas identiques, alors le message provient d'un autre client. Et c'est là que se pose le problème : on ne peut récupérer que le premier message de la file donc si le message que l'on veut obtenir se situe en deuxième position dans la file par exemple, alors on ne pourra jamais le récupérer.
    Comment puis-je faire pour parcourir la file de réception afin d'obtenir le bon message ?
    Existe-t-il une solution simple (seulement en utilisant winsock) ou alors dois-je me tourner vers des solutions beaucoup plus complexes (devoir recréer un protocole par exemple...) ?

    Voilà. Je précise aussi que je ne souhaite pas utiliser d'autres bibliothèques que celles fournies directement dans Windows 2000 (afin de garder la compatibilité avec ce système d'exploitation).
    Je vous remercie d'avance pour vos réponses.

    PS : si j'ai mal formulé le problème où omis des détails importants, merci de me le dire et je corrige aussitôt.

  3. #3
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Salut,

    Dans ce cas pourquoi ne mets tu pas en place ta propre gestion des packets? Les packets qui ne correspondent pas, que deviennent-ils?

  4. #4
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    Salut,

    Merci pour ta réponse rapide.
    Cependant, je ne comprend pas ce que tu veux dire par "mettre en place ta propre gestion des packets".

  5. #5
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Citation Envoyé par riked Voir le message
    (devoir recréer un protocole par exemple...) ?

    Avant que tu ne te lances dans du code à outrance , je voulais juste dire une fonction qui récupères les packets et qui appelle ta fonction sensée traité ces packets en fonction de leur destinataire! C'est ce que je ferai à ta place en tout cas mais je n'ai pas toutes les données du problème.

  6. #6
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    En effet, j'ai omis des détails. ^^
    J'ai oublié de dire que l'on ne possède que la taille de la donnée à récupérer et non la taille des autres.
    J'avais déjà pensé à ce système au début mais comme chaque donnée peut avoir une taille théoriquement infini, je ne peut pas créer de buffer permettant de les récupérer afin de les traiter dans une autre fonction.
    Aussi, le but du protocole UDP est bien entendu la rapidité donc je ne peut pas me permettre de récupérer toutes les données puis de les traiter à chaque fois que le serveur veut communiqué avec un client.

  7. #7
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Salut,

    Désolé, je comprends pas très bien où se situe ton problème. Soit c'est moi qui ne comprends pas du tout la problématique, soit tu n'as pas bien compris ce que je voulais dire.

    La fonction recvfrom va récupérer un datagram envoyé par un client. Ce qui veut dire que si tu as deux datagrams de deux clients différents, recvfrom ne va récupérer que le packet du premier, peu importe la taille de ton buffer. Tu connais la taille des infos qui ont été placés dans ton buffer grâce au retour de cette fonction.

    Puisque tu dois gérer plusieurs clients en même temps, ça implique en général un contexte multithread. Tu fais donc une fonction qui récupère les packets (en les vidants de la file d'attente), et tu l'envoies au thread qui gère ce client ou en crée un lorsqu'il s'agit d'un nouveau client.

    Vu que le message envoyé par le client a une taille variable, il te faut un moyen de savoir lorsque le message est terminé. Si tu n'as pas de moyens de le savoir, orientes toi plutôt vers un serveur TCP ou place des balises dans ton échange client/serveur. Lorsque tu communiques avec un serveur smtp, par exemple, CRLF est un marqueur servant à spécifier les champs. C'est à toi de gérer.

    C'est compliqué un serveur.

    Donc en gros tu as 2 solutions.

    I) Le gestionnaire.
    1) Mon gestionnaire récupère chaque packet sur le socket.
    2) Vérifie dans une table qu'il n'y a pas de threads en cours pour cette connection.
    3) Si oui, remplie le buffer de ce thread avec les datas et signale au thread que de nouvelles données sont arrivées ou alors, il est possible d'ouvrir une zone d'échange en mémoire et fermer cette zone pour signaler par un EOF que le client a finit avant d'effacer dans sa table de gestion l'entrée pour ce client (de façon a ce qu'un nouveau thread soit ouvert pour une nouvelle communication avec le même client). Tout dépend de ton protocole!
    Sinon , il crée un thread pour ce client.

    II) La fonction de traitement
    1) soit Lit le fichier d'échange (genre avec un while ( f != EOF ) fread(); ), soit lit son buffer jusqu'à ce qu'il ait ce qu'il souhaite en se fichant du reste et quitte (laissant le gestionnaire écrire dans le vent et libérer la mémoire à la fin).

    Bref, tout dépend de ton protocole et de ce avec quoi tu bosses mais sans balises, tu devrais voir plutôt du côté TCP.

    Ca t'es utile?

  8. #8
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    Encore merci de t'intéresser à mon problème mais il doit certainement y avoir une légère incompréhension au niveau du type de programme que je réalise.

    Citation Envoyé par baccali
    La fonction recvfrom va récupérer un datagram envoyé par un client. Ce qui veut dire que si tu as deux datagrams de deux clients différents, recvfrom ne va récupérer que le packet du premier, peu importe la taille de ton buffer. Tu connais la taille des infos qui ont été placés dans ton buffer grâce au retour de cette fonction.
    En effet, mais c'est bien là le problème : il ne récupère que le premier packet et je n'ai donc pas accès au deuxième ni aux suivants.

    Citation Envoyé par baccali
    Puisque tu dois gérer plusieurs clients en même temps, ça implique en général un contexte multithread. Tu fais donc une fonction qui récupère les packets (en les vidants de la file d'attente), et tu l'envoies au thread qui gère ce client ou en crée un lorsqu'il s'agit d'un nouveau client.
    Comme je l'ai dit dans mon tout premier message, le serveur n'existe pas : je créer simplement une bibliothèque qui va permettre ce serveur.
    Il n'est donc nullement question du multithread pour l'instant, cela étant laissé aux bons soins de l'utilisateur de la bibliothèque.
    Pour ce qui est de récupérer les packets, comme je l'ai déjà dit, je n'ai pas accès à la taille de ces packets et il n'est donc pas possible de les vider de la file de réception.

    Citation Envoyé par baccali
    Vu que le message envoyé par le client a une taille variable, il te faut un moyen de savoir lorsque le message est terminé. Si tu n'as pas de moyens de le savoir, orientes toi plutôt vers un serveur TCP ou place des balises dans ton échange client/serveur. Lorsque tu communiques avec un serveur smtp, par exemple, CRLF est un marqueur servant à spécifier les champs. C'est à toi de gérer.
    Sauf que mon programme est en fait une bibliothèque qui n'utilise pas de protocole spécifique (donc aucune réception de la taille avant la récupération des données) car elle est censée être réutilisable par n'importe quelle programme en direction de n'importe quelle autre programme (même ceux qui n'utilisent pas ma bibliothèque).
    Pourquoi m'orienter vers un serveur TCP alors qu'il est ici question de développer une bibliothèque permettant de faire un serveur UDP ?

    Citation Envoyé par baccali
    C'est compliqué un serveur.
    J'ai déjà fait de très nombreux serveurs et à aucun moment je n'ai trouvé cela compliqué.
    Par serveur, j’entends bien sûr la gestion du multithread, le protocole d’envoi/réception de messages et le protocole de sécurité.
    Par contre faire une bibliothèque qui s'adapte à n'importe quelle type de programme devant jouer le rôle de client/serveur et qui doit donc être standardisée au maximum, ça c'est compliqué.

    Citation Envoyé par baccali
    Donc en gros tu as 2 solutions.

    I) Le gestionnaire.
    1) Mon gestionnaire récupère chaque packet sur le socket.
    2) Vérifie dans une table qu'il n'y a pas de threads en cours pour cette connection.
    3) Si oui, remplie le buffer de ce thread avec les datas et signale au thread que de nouvelles données sont arrivées ou alors, il est possible d'ouvrir une zone d'échange en mémoire et fermer cette zone pour signaler par un EOF que le client a finit avant d'effacer dans sa table de gestion l'entrée pour ce client (de façon a ce qu'un nouveau thread soit ouvert pour une nouvelle communication avec le même client). Tout dépend de ton protocole!
    Sinon , il crée un thread pour ce client.

    II) La fonction de traitement
    1) soit Lit le fichier d'échange (genre avec un while ( f != EOF ) fread(); ), soit lit son buffer jusqu'à ce qu'il ait ce qu'il souhaite en se fichant du reste et quitte (laissant le gestionnaire écrire dans le vent et libérer la mémoire à la fin).

    Bref, tout dépend de ton protocole et de ce avec quoi tu bosses mais sans balises, tu devrais voir plutôt du côté TCP.
    ça c'est à l'utilisateur de la bibliothèque de gérer cela, mais ça ne s'applique pas dans mon cas.

  9. #9
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Citation Envoyé par riked Voir le message
    Encore merci de t'intéresser à mon problème mais il doit certainement y avoir une légère incompréhension au niveau du type de programme que je réalise.
    Effectivement je n'avais pas compris qu'il s'agissait d'une librairie (indépendante d'un protocole en plus!). C'est compliqué ce que tu veux réaliser. Dans tous les cas si tu veux mon avis, tu seras obligé d'être "intrusif" dans le code du serveur. Je veux dire par là que si ce n'est effectivement pas encore possible de parcourir les messages sans vider la queue, tu devras les retirer.



    Citation Envoyé par riked Voir le message
    Pour ce qui est de récupérer les packets, comme je l'ai déjà dit, je n'ai pas accès à la taille de ces packets et il n'est donc pas possible de les vider de la file de réception.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    packet_size = recvfrom(m_sock,buffer_data,size_data,0,(SOCKADDR*)&m_client[id].sin,&size_sin);
    Mais y'a un truc que je ne comprends pas, désolé d'être aussi lent à saisir la problématique (je comprends vite mais faut m'expliquer longtemps!! ), mais pourquoi est-ce si important de laisser le message dans la queue? Y'a d'autres threads (non gérés par la librairie) en écoute sur la socket?


    Citation Envoyé par riked Voir le message
    Pourquoi m'orienter vers un serveur TCP alors qu'il est ici question de développer une bibliothèque permettant de faire un serveur UDP ?
    Je parlais dans le cas où tu ne saurais pas quand tel ou tel client a finis d'envoyer ses données (si par exemple il envoie un autre packet sensé être traité comme un nouveau client).

  10. #10
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Citation Envoyé par riked Voir le message
    Pour ce qui est de récupérer les packets, comme je l'ai déjà dit, je n'ai pas accès à la taille de ces packets et il n'est donc pas possible de les vider de la file de réception.
    recvfrom ne récupère qu'un seul packet à la fois, quelque soit la taille de ton buffer (il tronque ce paquet et te le signale si ton buffer n'est pas suffisant!) Ce qui veut dire que si ton buffer fait 1024 bits et que tu as 2 message de 512 bits en attente, un recvfrom ne va récupérer que 512 bits, et celui d'après encore 512 bits.

  11. #11
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    Citation Envoyé par baccali
    mais pourquoi est-ce si important de laisser le message dans la queue? Y'a d'autres threads (non gérés par la librairie) en écoute sur la socket?
    C'est bien ça : la bibliothèque doit supporter le multithread donc si je récupère la donnée en la retirant de la file de réception, il pourrait y avoir des problèmes dans d'autres threads qui écoutaient au même moment.

    Citation Envoyé par baccali
    packet_size = recvfrom(m_sock,buffer_data,size_data,0,(SOCKADDR*)&m_client[id].sin,&size_sin);
    la fonction "recvfrom" renvoie le nombre d'octets lus (ou une erreur) et non la taille réel du packet. Si le buffer n'est pas assez grand, alors la valeur renvoyée par "recvfrom" n'a aucun sens.

    Citation Envoyé par baccali
    recvfrom ne récupère qu'un seul packet à la fois, quelque soit la taille de ton buffer (il tronque ce paquet et te le signale si ton buffer n'est pas suffisant!) Ce qui veut dire que si ton buffer fait 1024 bits et que tu as 2 message de 512 bits en attente, un recvfrom ne va récupérer que 512 bits, et celui d'après encore 512 bits.
    Je sais que "recvfrom" ne récupère qu'un seul packet à la fois mais comme je l'ai déjà dit, je ne connais pas la taille de ce packet et si je le retire de la file de réception, il faudra bien que je le stock quelque part (difficile sans savoir sa taille).
    En fait un autre problème est que je veux éviter la récupération en plusieurs temps afin d'obtenir des performances maximales (surtout en UDP, c'est primordiale pour les futurs utilisateurs si ils veulent réaliser des jeux en réseaux par exemple). Une des solutions serait donc de créer un buffer immense, ce que je ne souhaite pas.
    D'où ma question : existerait-il un moyen de parcourir la file de réception ?
    Une telle possibilité permettrait d'obtenir de grandes performances et d'enlever la contrainte du buffer.

  12. #12
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2009
    Messages
    172
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2009
    Messages : 172
    Points : 191
    Points
    191
    Par défaut
    Citation Envoyé par riked Voir le message
    JEn fait un autre problème est que je veux éviter la récupération en plusieurs temps afin d'obtenir des performances maximales (surtout en UDP, c'est primordiale pour les futurs utilisateurs si ils veulent réaliser des jeux en réseaux par exemple). Une des solutions serait donc de créer un buffer immense, ce que je ne souhaite pas.
    C'est tout le problème de la programmation : La performance contre la taille

    Sinon tu as 2 solutions :
    1) Performances
    A l'initialisation de la librairie tu crées un buffer de la taille du MTU et là tu es sûr que ton buffer sera suffisant sans faire de buffer trop énorme.
    2) Minimum en mémoire.
    Tu vérifies la taille du packet et tu realloc ton buffer si il est pas assez grand.

    De plus, comme tu dois d'abord vérifier l'émetteur du packet sans le retirer, tu auras déjà un recvfrom (MSG_PEEK) à faire. Il n'auras qu'à récupérer la taille à ce moment là.

    Moi ce que j'aurais fait à ta place, c'est installer un gestionnaire et demander au client de la librairie de t'indiquer des fonctions callback à en fonction de la provenance (ou ce que tu veux) du packet. Puisque de toute façon tu n'es pas sûre que eux, vont laisser le packet dans la queue.

    En tout cas perso, je ne connais pas de fonction qui te permettrait de donner simplement la taille d'un packet. Je ne pourrais donc pas plus t'aider que ce que je viens d'essayer de faire.

    Bon courage.

    Cordialement

  13. #13
    Nouveau Candidat au Club
    Homme Profil pro
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 1
    Points
    1
    Par défaut
    En général j'arrive toujours à obtenir les performances et une consommation de la mémoire correcte mais je dois avouer que ce cas me dépasse quelque peu.

    J'avais déjà pensé aux solutions que tu me propose mais c'est toujours soit les performances soit la mémoire, ce qui n'est pas vraiment une solution alors que la fonction que je recherche me permettrait d'obtenir les deux.

    Citation Envoyé par baccali
    En tout cas perso, je ne connais pas de fonction qui te permettrait de donner simplement la taille d'un packet. Je ne pourrais donc pas plus t'aider que ce que je viens d'essayer de faire.
    Peut-être que quelqu'un d'autre a la solution qui convienne à mon cas mais en attendant je vais utiliser une des solutions alternatives.
    En tout cas je te remercie vraiment d'avoir tenté de m'aider.

Discussions similaires

  1. [OL-2003] Erreur dans la réception des messages
    Par vlksoft dans le forum Outlook
    Réponses: 0
    Dernier message: 13/01/2010, 12h15
  2. Réception des messages dans le Socket
    Par rzayani dans le forum C++
    Réponses: 3
    Dernier message: 16/04/2008, 10h33
  3. Outlook 2007 Réception des messages de réponse
    Par Cecilenze dans le forum Outlook
    Réponses: 1
    Dernier message: 02/04/2007, 09h00
  4. réception des messages dans un chat en tcp
    Par je®ome dans le forum Réseau
    Réponses: 9
    Dernier message: 25/04/2006, 17h48
  5. réception des messages WM_PAINT
    Par enzoMetz dans le forum Windows
    Réponses: 2
    Dernier message: 19/06/2004, 20h15

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