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 :

Comportement de select et recvfrom sur socket UDP


Sujet :

Réseau C

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 15
    Points : 9
    Points
    9
    Par défaut Comportement de select et recvfrom sur socket UDP
    Bonjour à tous,

    Je recherche des informations sur le comportement concernant l'extrait de code ci-dessous :

    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
      const int        maxLength = 1;
      char             buffer[maxLength];
     
      while (TRUE == handle->running)
      {
        /* Initialize the fd_set structure and the greatest handle variable */
        FD_ZERO(&readfds);
        greatestHandle = -1;
        ...
        /* Add socket to the set */
        FD_SET(handle->sock, &readfds);
        greatestHandle = (handle->sock > greatestHandle) ? handle->sock : greatestHandle;
        /* Set up sleep time of select function to 50ms */
        tv.tv_sec = 0;
        tv.tv_usec = 50000;
        /* Wait for 50ms or an event */
        if (0 != select(greatestHandle + 1, &readfds, NULL, NULL, &tv))
        {
          ...
          /* Does the event come from UDP socket? */
          if (FD_ISSET(handle->sock, &readfds))
          {
            remoteLength = sizeof(struct sockaddr_in);
    	length = recvfrom(handle->sock, buffer, maxLength, MSG_DONTWAIT, (struct sockaddr *)&handle->remote, &remoteLength);
            length = write(handle->inHandle, buffer, length);
          }
        }
      }
    Tout fonctionne bien si le paquet fait moins de 10 caractères. Toutefois, je m'attendais à ce que la fonction select repositionne l'indicateur du socket quand il reste des caractères en réception (je reçois 24 caractères, je ne lit que 9 caractères d'où trois lectures et, malheureusement, la fonction recvfrom n'est appelé qu'une seule fois).

    C'est peut-être normal. Qu'en pensez-vous ?

    D'avance merci

    Thoma

  2. #2
    Membre éclairé
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Points : 719
    Points
    719
    Par défaut
    Bonjour,

    Sur un socket UDP, la fonction recvfrom() copie le contenu du dernier message reçu dans le buffer que tu as passé en paramètre (buffer dans ton exemple). Si le buffer est trop petit, elle copie ce qu'elle peut et efface le reste.

    Je n'ai pas trouvé de lien qui explique ça clairement. La manpage de recvfrom (http://linux.die.net/man/2/recvfrom) dit simplement que ça dépend du type de socket, ce qui est vrai, mais elle ne dit pas clairement que c'est le cas en UDP.

    A mon humble avis, la raison tient à deux choses :
    - UDP est protocole orienté message : les messages sont stocké dans l'ordre d'arrivée et un message est soit entièrement arrivé, soit pas arrivé du tout, à la différence des protocoles types flux (genre TCP).
    - UDP est non connecté, donc potentiellement recvfrom() peut te retourner, à chaque appel, des données provenants d'émetteurs différents.

    Bonne chance.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 15
    Points : 9
    Points
    9
    Par défaut
    Bonjour,

    J'avais pensé à ce type de comportement.

    Reste que je suis sur une cible embarquée et que les octets coûtent cher par là.
    Existe-t-il une autre manière de récupèrer les données ou est-ce que je dois vivre avec un buffer de réception de 1500 octets ?

    Je penche malheureusement pour la seconde et cela ne m'arrange pas du tout.

    Je sais bien que les données peuvent provenir de différentes sources à chaque appel et que ce n'est pas orienté flux. Toutefois, les données non lues pourraient, malgré cela, être représentées jusqu'à épuisement.

    Thoma

  4. #4
    Membre éclairé
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Points : 719
    Points
    719
    Par défaut
    La réponse courte c'est non, pas vraiment.
    En fait, si le protocole (au niveau application) que tu essayes d'implémenter utilises UDP, tu dois avoir une façon de connaitre a l'avance la taille maximale d'un paquet. Tu peux eventuellement t'aider du flag MSG_PEEK pour lire sans effacer le début du paquet, mais le problème reste entier : un appel à recvfrom() doit pouvoir écrire le contenu entier du message dans le buffer de destination.

    Toutefois, je lis ton code et je me rends compte que tu ne fais que renvoyer les données lues à un appel à write().
    Et là ça me pousse à réfléchir : au fond ce que tu essayes de faire, c'est juste copier des données depuis un descripteur vers un autre.
    Je ne sais pas sous quel système tu travailles, mais les Linux relativement récents ont une fonction nommées sendfile()
    http://man7.org/linux/man-pages/man2/sendfile.2.html
    qui fait exactement ça : copier des données d'un descripteur vers un autre. Et comme c'est le noyau qui le fait, il fait ça bien.

    NOTE : je n'ai jamais utilisé cette fonction, je n'ai appris son existence qu'il y a quelques minutes, en recherchant une réponse pour ton cas. Je sais pas si ça s'applique vraiment à ta situation.

    EDIT : il y a aussi la fonction splice() qui semble encore plus adaptée :
    http://man7.org/linux/man-pages/man2/splice.2.html

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 15
    Points : 9
    Points
    9
    Par défaut
    Bonsoir,

    Pour un essai, j'ai remplacé le code ci-dessous avec la fonction splice.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	remoteLength = sizeof(struct sockaddr_in);
    	length = recvfrom(handle->sock, buffer, maxLength, MSG_DONTWAIT, (struct sockaddr *)&handle->remote, &remoteLength);
            length = write(handle->inHandle, buffer, length);
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    	if (-1 == splice(handle->sock, NULL, handle->inHandle, NULL, 10240, SPLICE_F_NONBLOCK))
    	{
    	  perror(NULL);
    	}
    Malheureusement, la fonction me retourne Invalid argument.
    Et d'après la documentation, cela signifie que le filesystem ne supporte pas la fonction sachant que les autres cas possibles ne s'applique pas à mon avis.

    La fonction sendfile ne correspond pas trop à mon besoin car il faut indiquer une longueur alors que splice dispose d'un modificateur permettant de faire des appels non-bloquant (SPLICE_F_NONBLOCK).

    Thoma

Discussions similaires

  1. Thread sur socket udp
    Par reitsab dans le forum WinDev
    Réponses: 2
    Dernier message: 23/02/2010, 14h57
  2. Envoi d'une matrice sur socket UDP
    Par caubios dans le forum Qt
    Réponses: 0
    Dernier message: 10/02/2010, 20h10
  3. [Réseau] select() et accept() sur plusieurs sockets
    Par Higestromm dans le forum C++
    Réponses: 13
    Dernier message: 13/10/2008, 10h18
  4. Les sockets UDP sur internet?
    Par chm0105 dans le forum C++Builder
    Réponses: 9
    Dernier message: 10/08/2007, 09h55
  5. Notion sur Socket UDP
    Par oxor3 dans le forum Développement
    Réponses: 3
    Dernier message: 05/04/2004, 01h19

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