2009-03-24 15 views
6

Estoy trabajando en un programa confiable de transferencia de archivos que usa UDP. (. Para un curso en la red de la computadora)¿Las llamadas sucesivas a recvfrom() pierden datos?

Mi pregunta es la siguiente - así, tenga en cuenta este escenario:

  1. remitente tiene (por ejemplo) 12 bytes de datos para enviar. Por lo que el emisor realiza esta llamada:

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

    Esto envía los 12 bytes de datos de manera fiable. Los primeros 4 bytes de estos datos pasan a ser un campo de "longitud de mensaje". En este caso, los primeros 4 bytes pueden tener el valor 0x0000000C

  2. El receptor desea leer los primeros 4 bytes utilizando recvfrom(). Al ver que el tamaño del segmento es de 12 bytes, quiere leer los 8 bytes restantes. Por lo que el receptor podría tener este aspecto:

    /* 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); 
    

Cuando ejecuta este código, puede recibir los primeros 4 bytes sin ningún problema. Pero cuando trato de buscar los datos restantes, esos datos parecen estar perdidos. En mi salida, estoy recibiendo basura, parece que una parte del siguiente 12 bytes que el remitente está enviando a() - ing.

¿Este comportamiento es esperado? Es decir, si una sola llamada recvfrom() no lee todos los datos que se enviaron, ¿no se garantiza que esos datos (los 8 bytes restantes) estén disponibles para mí?

Parece que el método estándar para enviar un encabezado de segmento (incluido su tamaño), seguido de la carga útil, no funciona. ¿Eso significa que tengo que enviar 2 segmentos separados, uno que solo contiene información de encabezado y luego un segundo segmento con la carga útil? O solo estoy usando estas llamadas al sistema de forma incorrecta (o hay una bandera o setsockopt() que me falta?)

+1

El punto es que UDP es un protocolo de estilo de paquete, no un protocolo de estilo de canal. Por lo tanto, debe proporcionarle todo el paquete de una vez, para que pueda saber dónde termina el paquete.De lo contrario, no tendrías ni idea de qué estaba pasando realmente. –

Respuesta

7

Desde el recv (2) página del manual:

Si un mensaje es demasiado largo para caber en el buffer provisto , los bytes en exceso pueden ser descartados dependiendo del tipo de socket del que se recibe el mensaje.

Esto es lo que parece que le está sucediendo a usted.

Debe tener un búfer del tamaño máximo de mensaje y leer esa cantidad. Leerá solo un datagrama y se devolverá la longitud. A continuación, puede analizar la longitud desde el frente del búfer y validarlo contra lo que recvdesde (2) devuelto.

+0

Es decir, ¿el tamaño máximo del segmento debe ser fijo y conocido tanto por el cliente como por el servidor? – poundifdef

+0

El tamaño máximo de un datagrama UDP es de 64 Kb. Es posible que no pueda enviar tanto, dependiendo del tamaño del búfer de envío. (MSS es un término TCP, BTW). – camh

+2

Como está diseñando este protocolo, no dude en elegir un tamaño máximo. UNP Vol1, 3rd Edition recomienda usar un buffer que es un byte mayor que el mensaje más grande que esperas recibir. Si recvfrom() devuelve un valor igual a la longitud de su buffer, se debe tratar como un error. – sigjuice

2

Otro método es realizar una recuperación ficticia con el indicador MSG_PEEK. Si bien el tamaño devuelto es el mismo que el tamaño de tu búfer (o más), obtén un búfer más grande y vuelve a intentarlo. A continuación, vuelva a realizar la recepción de nuevo (sin el indicador MSG_PEEK) para eliminar el mensaje del búfer UDP.

Pero, por supuesto, esto es bastante ineficiente y no debe hacerse cuando se puede simplemente decidir un tamaño máximo de paquete.

+0

Esto no funciona. Supongamos que usted hace el recmfrom ficticio y luego, antes de hacer el recvfrom "real", el datagrama fue descartado. A continuación, obtendrá el siguiente datagrama en un búfer de tamaño incorrecto, posiblemente truncándolo. –

Cuestiones relacionadas