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 :

Fonction select() les données ne s'envoient que lors d'un recv !


Sujet :

C

  1. #1
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut Fonction select() les données ne s'envoient que lors d'un recv !
    Salut !

    Voila j'utilise la fonction select pour ne pas bloquer sur un recv lors d'une connexion TCP.
    Tout cela fonctionne parfaitement, je recoie uniquement quand le client envoie.

    Le probleme c'est à l'inverse quand moi je veux envoyer des données, il n'y a rien qui sort tant que je n'ai pas recu de données....

    Pourtant j'ai bien placé mon send à l'exterieur du if(FD_ISSET... donc le send devrait s'executer a chaque tour de boucle..

    Voici le bout de code (juste la boucle) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
          do
          {
     
               FD_ZERO(&EnsembleLecture); // A chaque tour de boucle on remet a zero l'ensemble
               FD_SET(StructureEchangee->IDSocketClient, &EnsembleLecture); // On met l'id de la socket client dans l'emnsemble de lecture
     
               CodeErreur = select(StructureEchangee->IDSocketClient + 1, &EnsembleLecture, NULL, NULL, NULL); // On vérifie s'il y a des données dans la socket
               if (CodeErreur < 0)
               {
                   sprintf(TexteErreur,"Impossible de faire un select sur la socket client du à l'erreur : %d",WSAGetLastError());
                   MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                   return -1;
               }
     
               if(FD_ISSET(StructureEchangee->IDSocketClient, &EnsembleLecture)) // Si il y a des données dans la socket
               {
                    MessageBox(NULL,"select : il y a des donnees","Erreur Générale !",MB_OK | MB_ICONERROR);
                    LongueurMessage = recv(StructureEchangee->IDSocketClient, (char*)&StructRecue, sizeof(StructSocket),0); // On recoit la position
                    if (LongueurMessage == SOCKET_ERROR)
                    {
                        sprintf(TexteErreur,"serv: Impossible de recevoir les données du à l'erreur : %d",WSAGetLastError());
                        MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                        closesocket(StructureEchangee->IDSocketClient);
                        closesocket(StructureEchangee->IDSocketServeur);
                        return -1;
                    }
     
                    StructureEchangee->PositionJoueurDistant = StructRecue.PositionJoueurLocal; // On met la position du joueur distant dans notre structure locale
     
                    if(LongueurMessage == 0) // Recv vaut 0 si on a recu un shutdown
                    {
                        StructureEchangee->ActiverTransmission = FALSE; // A notre tour on desactive la transmission ce qui va envoyer un shutdown dans le thread d'emission
                        sleep(2000); // On fait une pause de 1s avant de sortir de la boucle car sinon on ferme la socket avant d'envoyer le shutdown (thread d'envoi)
                    }   
                }
     
      //          if((StructureEchangee->PositionJoueurLocal.x != PositionJLocalPrecedente.x) || (StructureEchangee->PositionJoueurLocal.y != PositionJLocalPrecedente.y) || (!StructureEchangee->ActiverTransmission))
       //         { // Si notre position a changé, ou si on recoit une demande d'arret de transmission de l'application principale
     
                     LongueurMessage = send(StructureEchangee->IDSocketClient, (char*)StructureEchangee, sizeof(StructSocket),0);
                     sprintf(TexteErreur,"serv: longeur envoi : %d",LongueurMessage);
                         MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONWARNING);
                     if (LongueurMessage == SOCKET_ERROR)
                     {
                         sprintf(TexteErreur,"serv: Impossible d'envoyer les données du à l'erreur : %d",WSAGetLastError());
                         MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                         return -1;
                     }
     
                     if(!StructureEchangee->ActiverTransmission) // Si on doit arreter la transmission
                     {
                          CodeErreur = shutdown(StructureEchangee->IDSocketClient, 1); // On shutdown les operations sur la socket puisqu'on a 500ms de delai
                          sprintf(TexteErreur,"serv: valeur shutdown : %d",CodeErreur);
                         MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONWARNING);
                          if (LongueurMessage == SOCKET_ERROR)
                          {
                               sprintf(TexteErreur,"Impossible de shutdown la socket du a l'erreur : %d",WSAGetLastError());
                               MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                               return -1;
                          }
                     }
                     else
                     {
                         PositionJLocalPrecedente.x = StructureEchangee->PositionJoueurLocal.x; // On remplace la position precedente par la position actuelle
                         PositionJLocalPrecedente.y = StructureEchangee->PositionJoueurLocal.y; // Ca evite d'envoyer des paquets alors qu'on ne bouge pas
                     }
     
     
          //      }
     
          } while (LongueurMessage != 0); // Shutdown terminé dans les deux sens, on ferme proprement les sockets
    Merci!

  2. #2
    Invité
    Invité(e)
    Par défaut Select est bloquant
    Bonjour,

    ton problème vient du fait que select soit bloquant par default : tant qu'il ne se passe rien sur les fd qu'elle surveille, elle stoppe le processus. Comme tu ne lui donne que des fd en lecture à surveiller, et aucun timeout, elle bloquera tant que tu ne recevras rien à lire.

    Pour obtenir le comportement que tu souhaites, tu as deux solutions :
    1) definir le temps maximum pendant lequel tu souhaites que select bloque. Pour cela, il faut utiliser le dernier argument de select, qui utilise une structure timeval.

    2) lui dire de surveiller les fd en écriture que tu souhaites utiliser, en lui envoyant un fd_set en troisième argument. Dans ce cas, select sortira de sa boucle dès que le fd sera prêt en écriture. Dans la majorité des cas, les fd sont toujours prêts, et select ne bloquera quasiment pas la boucle, ce lui fait perdre une grande partie de son intérêt. Tu ne dois donc lui donner à surveiller des fd en écriture que si tu as des données à écrire.

    Dis-moi si tu as besoin de précisions,
    Nathan

  3. #3
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    Oui effectivement car je ne vois pas comment lui envoyer un message *Localement* que j'ai besoin d'écrire sur la socket.

    En fait j'ai uniquement besoin d'écrire dans ce cas la :
    StructureEchangee->PositionJoueurLocal.x != PositionJLocalPrecedente.x) || (StructureEchangee->PositionJoueurLocal.y != PositionJLocalPrecedente.y)

    C'est à dire si je change au clavier la position de mon bonhome.

    Mais étant donné que la fonction select bloque, je ne vois pas comment faire ce if, mis à part dans un autre thread, dans ce cas la auant mettre la fonction send dans l'autre thread..

  4. #4
    Invité
    Invité(e)
    Par défaut
    Je ne vois pas le problème, j'ai du mal comprendre :

    Puisque tu lis la structure à chaque tour de boucle, tu sais si ta condition est remplie avant d'entrer dans le select. Dès lors :

    - tu déclares un fd_set pour les fd en écritures, que tu mets à FD_ZERO
    - tu effectues la lecture et traite l'information
    - si la condition est réalisée, tu fais un FD_SET en ecriture
    - sinon tu ne le fais pas.

  5. #5
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    Oui mais justement le select me bloque dans ma boucle.

    Imaginos que je ne recoive pas de données.
    Je suis donc bloqué sur le select.
    Maintenant je bouge mon bonhome avec le clavier.
    Je veux que sa position soit envoyée, sauf qu'il n'y a aucun moyen de le faire puisque je suis toujours bloqué sur mon select!

    Donc c'est meme pas le problème d'envoyer, un simple test est impossible du fait du blocage !

    Ou alors je n'ai pas compris ce que tu as voulu dire

  6. #6
    Invité
    Invité(e)
    Par défaut
    Donc en fait ton problème est dans le client, pas dans le serveur ? J'avais compris le contraire

    Dans ce cas, ouvre le fd en mode non bloquant, je cite le man : If no messages are available at the socket and O_NONBLOCK is set on the socket's file descriptor, recv() shall fail and set errno to [EAGAIN] or [EWOULDBLOCK].

  7. #7
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    Oui en fait sur le serveur j'ai besoin de faire des send et des recv, en fait c'est exactement le meme code coté client mais avec un connect au lieu de listen+accept.

  8. #8
    Invité
    Invité(e)
    Par défaut
    A moins de lancer select dans un autre thread, la seule solution que je vois c'est donc un read non bloquant pour le client, ce qui aura en plus l'avantage de la concision au niveau du code.

  9. #9
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    Salut,

    Bon ben ca marche !

    Par contre, alors que mon shutdown pour fermer proprement ma connexion fonctionnait, maintenant c'est plus le cas

    Lorsque je lance ma séquence de shutdown, la fonction shutdown me renvoie -1 avec le code erreur WSA 10035, qui est justement WSAEWOULDBLOCK ! (donc du a mon socket non bloquant)

    Voici l'intégralité de ma boucle, rien a changé par rapport à avant les sockets non bloquant mis à part l'appel a ioctlsocket avant la boucle :
    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
          ioctlsocket(ParametresSocket->IDSocketClient, FIONBIO, &ModeNonBloquant); // On active le mode non bloquant sur la socket client
     
          do
          {
    /* Reception des données */
                 LongueurMessage = recv(ParametresSocket->IDSocketClient, (char*)&InfosRecues, sizeof(StructEchangee),0); // On recoit la position
     
                 if ((LongueurMessage == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) // Si on a une erreur mais que ce n'est pas du au bloquage de la socket
                 {
                     sprintf(TexteErreur,"Impossible de recevoir les données du à l'erreur : %d",WSAGetLastError());
                     MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                     closesocket(ParametresSocket->IDSocketClient);
                     closesocket(ParametresSocket->IDSocketServeur);
                     return -1;
                 }
     
                 ParametresSocket->PositionJoueurDistant.x = InfosRecues.PositionJoueurLocal.x; // On met la position du joueur distant dans notre structure locale
                 ParametresSocket->PositionJoueurDistant.y = InfosRecues.PositionJoueurLocal.y;
     
                 if(LongueurMessage == 0) // Recv vaut 0 si on a recu un shutdown
                 {
                    ParametresSocket->ActiverTransmission = FALSE; // A notre tour on desactive la transmission ce qui va envoyer un shutdown en reponse
                 }   
     
    /* Envoi des données */
                if(((ParametresSocket->PositionJoueurLocal.x != InfosTransmises.PositionJoueurLocal.x) || (ParametresSocket->PositionJoueurLocal.y != InfosTransmises.PositionJoueurLocal.y)) && (ParametresSocket->ActiverTransmission))
                { // Si notre position a changé et qu'on est pas en train de shutdown
                     InfosTransmises.PositionJoueurLocal.x = ParametresSocket->PositionJoueurLocal.x; // On remplace la position precedente par la position actuelle
                     InfosTransmises.PositionJoueurLocal.y = ParametresSocket->PositionJoueurLocal.y; // Ca evite d'envoyer des paquets alors qu'on ne bouge pas
     
                     LongueurMessage = send(ParametresSocket->IDSocketClient, (char*)&InfosTransmises, sizeof(StructEchangee),0);
     
                     if (LongueurMessage == SOCKET_ERROR)
                     {
                         sprintf(TexteErreur,"Impossible d'envoyer les données du à l'erreur : %d",WSAGetLastError());
                         MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                         closesocket(ParametresSocket->IDSocketClient);
                         closesocket(ParametresSocket->IDSocketServeur);
                         return -1;
                     }
                }
     
    /* Desactivation de la connexion */          
                if(!ParametresSocket->ActiverTransmission) // Si on doit arreter la transmission
                {
                     CodeErreur = shutdown(ParametresSocket->IDSocketClient, 1); // On shutdown les operations sur la socket (envoie d'un bit FIN)
                     if (LongueurMessage == SOCKET_ERROR)
                     {
                         sprintf(TexteErreur,"serv:Impossible de shutdown la socket du a l'erreur : %d",WSAGetLastError());
                         MessageBox(NULL,TexteErreur,"Erreur Générale !",MB_OK | MB_ICONERROR);
                         closesocket(ParametresSocket->IDSocketClient);
                         closesocket(ParametresSocket->IDSocketServeur);
                         return -1;
                     }
                }
     
                sleep(30);
     
          } while (LongueurMessage != 0); // Shutdown terminé dans les deux sens, on ferme proprement les sockets

  10. #10
    Invité
    Invité(e)
    Par défaut
    Il paraît que rendre une socket non bloquante est la porte ouverte à des erreurs bizarres, c'est pourquoi je te conseille d'utiliser le flog O_NONBLOCK au moment de faire le open, sans compter que ça te fait un appel système de moins :-)

    Pour ce qui concerne la fonction shutdown je ne sais pas t'aider, sinon pour te dire qu'elle n'est pas nécessaire côté client : en effet, dès que tu appelles close sur le fd de la socket, le serveur fera un read de taille 0 qui lui indiquera que la socket a été fermée côté client.

  11. #11
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    Il me semble avoir vu un shutdown des deux cotés sur les tutoriels que j'ai pu lire!
    En fait le serveur va recevoir 0, quand le client envoie shutdown, mais a son tour il faut que le client recoive 0 pour indiquer la fin de la procedure FIN/ACK/FIN/ACK

    Le if est présent dans le cas ou ce soit la personne qui est "serveur" qui ferme lui meme la connexion, c'est juste une question de langage, il est serveur sans le savoir car il en faut bien un.

    fcntl n'est pas plutot pour UNIX ?

  12. #12
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    J'ai essayé de repasser ioctlsocket(.....); avec un ModeNonBloquant à FALSE, juste avant l'appel à shutdown, et cette fois si le shutdown me fait quand meme un SOCKET_ERROR avec une erreur WSA = 0

    ???

  13. #13
    Invité
    Invité(e)
    Par défaut
    Jette un oeil sur ce site, qui est consacré aux sockets sous windows en mode non-bloquant, cela devrait te guider.

    Par ailleurs si tu le peux je te conseille de separer client et serveur, afin d'avoir des programmes spécialisés, avec un serveur lancé à part des clients. Ou alors lance toi dans une architecture décentralisée et construit un seul programme

  14. #14
    Membre régulier Avatar de theclem35
    Homme Profil pro
    Technicien Réseaux & Télécommunications
    Inscrit en
    Décembre 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vendée (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien Réseaux & Télécommunications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Décembre 2007
    Messages : 148
    Points : 86
    Points
    86
    Par défaut
    tout est dans le meme programme

    mon erreur était :
    CodeErreur = shutdown(ParametresSocket->IDSocketClient, 1);
    if (LongueurMessage == SOCKET_ERROR)

    Je testais la mauvaise variable

  15. #15
    Invité
    Invité(e)
    Par défaut
    J'aurais du mieux regarder

    Je viens de terminer un mini-irc pour mon école, et je n'ai pas eu le temps d'implémenter une architecture décentralisée, du coup je peux te demander comment tu as fait ? Je suis curieux

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

Discussions similaires

  1. [Débutant] récupérer les données d'un fichier que l'on sélectionne
    Par olivier59820 dans le forum MATLAB
    Réponses: 2
    Dernier message: 28/06/2011, 13h57
  2. Réponses: 5
    Dernier message: 22/06/2009, 23h10
  3. [XL-2003] Comment utiliser la fonction "Actualiser les données"
    Par P96O1004 dans le forum Excel
    Réponses: 10
    Dernier message: 27/05/2009, 14h05
  4. Fonction garde les donnés des call précédent.. que faire?
    Par tux94 dans le forum Programmation et administration système
    Réponses: 0
    Dernier message: 02/04/2008, 14h56
  5. Réponses: 2
    Dernier message: 24/04/2007, 20h04

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