2010-02-10 17 views
8

Tengo un problema para entender qué devuelve recv()/recvfrom() de un socket UDP no bloqueado.programación de socket udp sin bloqueo en C: ¿qué obtengo?

un poco más específico y en comparación con TCP (por favor, corríjanme si me equivoco):

  • Un socket de bloqueo (TCP o UDP) no volverá a partir de un recv() hasta que haya hay algunos datos en el búfer. Esto podría ser una cantidad de bytes (TCP) o un datagrama completo (UDP).

  • Un socket TCP no bloqueante devuelve EWOULDBLOCK (linux)/WSAEWOULDBLOCK (Windows) o los bytes que están actualmente en el búfer. Como los datos TCP son una secuencia, no importa cuántos bytes se devuelvan.

Ahora la pregunta:

  • un socket UDP no bloqueante también devuelve WOULDBLOCK (Linux)/WSAEWOULDBLOCK (Windows) Si no hay datos disponibles. Pero si hay datos disponibles, ¿un socket UDP sin bloqueo devuelve solo algunos bytes, lo que podría significar que solo obtiene la mitad de un datagrama O un socket UDP siempre devuelve datagramas completos?

Editar:

Lo que quiero decir con "la mitad de un datagrama" es: ¿qué ocurre si llamo a recv() sólo en el momento en que la toma está recibiendo actualmente un datagrama. En ese momento, hay algunos bytes en el búfer, pero el datagrama aún no está completo.

Sus explicaciones y comentarios son apreciados. ¡Gracias!

Respuesta

8

Finalmente, una excusa para sacar mis libros de Stevens de mis viejos cuadros de oficina.

Siempre que el búfer sea lo suficientemente grande, las tomas de corriente estándar de Berkeley recv() y recvfrom() nunca devolverán un datagrama parcial. El datagrama no está disponible para la aplicación hasta que el kernel haya recibido y reensamblado por completo el datagrama.

Curiosamente, y esto no es mucho (cualquier?) De un problema hoy en día, otras interfaces de programación de la red no están de acuerdo en el comportamiento cuando el búfer proporcionado es demasiado pequeño:

La versión tradicional de Berkeley de los sockets API trunca el datagrama, descartando cualquier exceso de datos. Si la aplicación se notifica depende de la versión. (4.3BSD Reno y luego puede notificar a la aplicación que el datagrama fue truncado.)

La API de sockets en SVR4 (incluido Solaris 2.x) no trunca el datagrama. Cualquier exceso de datos se devuelve en lecturas posteriores. No se notifica a la aplicación que se están realizando múltiples lecturas desde un solo datagrama UDP.

La API de TLI no descarta los datos. En su lugar, se devuelve un indicador que indica que hay más datos disponibles, y las lecturas posteriores de la aplicación devuelven el resto del datagrama.

(Stevens, TCP/IP Illustrated, Volumen 1, pág. 160)

+0

Parece que es posible pasar y recibir un indicador MSG_TRUNC a 'recvmsg' en Linux. Documentado en la página de manual 'recv (2)'. En otra nota, tal vez estoy malinterpretando, pero solo puedo encontrar el comportamiento de descarte documentado en la página de manual para 'socket (2)', que solo lo menciona para los sockets 'SOCK_SEQPACKET'. Nunca los he usado personalmente. –

+0

'MSG_TRUNC' como argumento para' recv (2) 'no es estándar. No está disponible ni en FreeBSD ni en Mac OS X (los sistemas a los que tengo acceso en este momento, probablemente sean ciertos para otros). 'MSG_TRUNC' está disponible en Linux, FreeBSD y Mac OS X en el miembro' flags' de 'struct msghdr' pasado a' recvmsg (2) '. En cualquier caso, el datagrama se truncará si el búfer pasado no es lo suficientemente grande, incluso con 'recv (2)' en Linux. La persona que llama debe verificar el valor de retorno y compararlo con el tamaño del búfer si se usa 'MSG_TRUNC' allí. Sabrá que la información se perdió, pero aún está perdida. –

+0

¡Gracias! Esto significa que UDP está ** realmente ** orientado a paquetes ... – Uwe

0

Creo que obtiene exactamente uno o cero datagramas. Pero no puedo respaldar esto en este momento. Tal vez alguien más podría proporcionar una buena referencia?

Editar: Estoy bastante seguro de que no puede recibir la mitad de un datagrama. O el datagrama ha llegado al buffer, o no.

+0

creo que es correcto. También puede obtener un error si no cabe en el búfer que suministró, o si sus mbufs no son lo suficientemente grandes (sucede a veces con datagramas fragmentados grandes). –

+0

Los mbufs son una estructura de datos del kernel BSD. No están expuestos a un usuario. –

1

Sí, UDP simplemente devuelve qué datos se transmitieron en ese datagrama. UDP no está orientado a flujos como TCP. Los datagramas son transmisiones discretas, y en realidad no están vinculados a otros datagramas de ninguna manera. Esa es la razón por la cual la opción de socket para TCP es SOCK_STREAM.

El lado bueno de esto es que puede tener una idea de las transmisiones por separado, lo que no es realmente fácil de hacer con TCP.

+0

Gracias a ambos. Conozco la diferencia entre TCP orientado a flujo (SOCK_STREAM) y UDP orientado a paquete (SOCK_DGRAM). Simplemente no estaba seguro de si un protocolo UDP no bloqueante() estaría bien orientado a paquetes. Para citar la página de manual de recv(): ... Si no hay mensajes disponibles en el socket, las llamadas de recepción esperan a que llegue un mensaje, a menos que el socket no sea de bloqueo (vea fcntl (2)), en cuyo caso se devuelve el valor -1 y la variable externa errno se establece en EAGAIN o EWOULDBLOCK. ** Las llamadas de recepción normalmente devuelven cualquier dato disponible, ** ... – Uwe

Cuestiones relacionadas