2009-03-24 4 views
6

Je travaille sur un programme de transfert de fichiers fiable qui utilise UDP. (. Pour un cours en réseau informatique)Les appels successifs à recvfrom() perdent des données?

Ma question est - bien, pensez à ce scénario:

  1. expéditeur a (par exemple) 12 octets de données à envoyer. L'expéditeur exécute donc cet appel:

    sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr)); 
    

    Ceci envoie les 12 octets de données de manière non fiable. Les 4 premiers octets de ces données se trouvent être un champ "longueur de message". Dans ce cas, les 4 premiers octets peuvent avoir la valeur 0x0000000C

  2. Le récepteur veut lire les 4 premiers octets en utilisant recvfrom(). Voyant que la taille du segment est de 12 octets, il veut lire les 8 octets restants. Ainsi, le récepteur pourrait ressembler à ceci:

    /* read the segment size */ 
    recvfrom(sockfd,&buf,4,0,(struct sockaddr *)&cliaddr,&len); 
    
    /* do some arithmetic, use bzero(), etc */ 
    
    /* read the rest of the data */ 
    recvfrom(sockfd,&buf,8,0,(struct sockaddr *)&cliaddr,&len); 
    

Lorsque j'exécute ce code, je peux recevoir les 4 premiers octets sans problème. Mais quand j'essaye d'aller chercher les données restantes, ces données semblent être perdues. Dans ma sortie, je reçois des déchets - il ressemble à une partie des prochains 12 octets que l'expéditeur est sendto() - ing.

Est-ce que ce comportement est attendu? C'est-à-dire, si un seul appel recvfrom() ne lit pas toutes les données envoyées, n'est-il pas garanti que ces données (les 8 octets restants) sont disponibles pour moi?

Il semble que la méthode standard d'envoi d'un en-tête de segment (y compris sa taille), suivie de la charge utile, ne fonctionne pas. Est-ce que cela signifie que j'ai besoin d'envoyer deux segments séparés - un qui ne contient que des informations d'en-tête, et un deuxième segment avec la charge utile? Ou suis-je juste en utilisant ces syscalls incorrectement (ou est-il un drapeau ou setsockopt() que je suis absent?)

+1

Le point est que UDP est un protocole de style de paquet, pas un protocole de style pipe. Ainsi, il doit vous donner le paquet entier en un coup, de sorte que vous puissiez savoir où le paquet commence une fin.Sinon, vous n'auriez aucune idée de ce qui se passait vraiment. –

Répondre

7

De recv (2) page man:

Si un message est trop long pour insérer dans le tampon fourni , octets excédentaires peuvent être mis au rebut en fonction du type de socket le message est reçu.

Ceci ressemble à ce qui vous arrive.

Vous devriez avoir un tampon de la taille maximale du message et lire cette quantité. Vous ne lirez qu'un seul datagramme et la longueur sera retournée. Vous pouvez ensuite analyser la longueur à partir du front de la mémoire tampon et la valider par rapport à ce que recvfrom (2) a renvoyé.

+0

En d'autres termes, la taille de segment maximale doit être fixe et connue à l'avance par le client et le serveur? – poundifdef

+0

La taille maximale d'un datagramme UDP est de 64 Ko. Vous ne pouvez peut-être pas envoyer autant, en fonction de la taille du tampon d'envoi. (MSS est un terme TCP, BTW). – camh

+2

Puisque vous concevez ce protocole, n'hésitez pas à choisir une taille maximale. UNP Vol1, 3e édition recommande d'utiliser un tampon d'un octet plus grand que le plus gros message que vous pensez recevoir. Si recvfrom() renvoie une valeur égale à la longueur de votre buffer, elle doit être traitée comme une erreur. – sigjuice

2

Une autre méthode consiste à effectuer un recvfrom factice avec l'indicateur MSG_PEEK. Alors que la taille retournée est la même que la taille de votre tampon (ou plus), obtenez un tampon plus grand et réessayez. Puis recvfrom à nouveau (sans l'indicateur MSG_PEEK) pour supprimer le message du tampon UDP. Mais, bien sûr, cela est assez inefficace et ne devrait pas être fait lorsque vous pouvez décider d'une taille de paquet maximum.

+0

Cela ne fonctionne pas. Supposons que vous fassiez le repli factice, puis avant de faire le "réel" recvfrom, le datagramme a été rejeté. Vous obtiendrez alors le prochain datagramme dans un tampon de mauvaise taille, en le tronquant peut-être. –

Questions connexes