tcp - number - udp 123
Sockets de bloqueo: ¿cuándo, exactamente, regresa "send()"? (5)
¿Cuándo, exactamente, la función BSD socket send()
regresa a la persona que llama?
En el modo sin bloqueo , debería regresar de inmediato, ¿correcto?
En cuanto al modo de bloqueo , la página man dice:
Cuando el mensaje no encaja en el búfer de envío del zócalo, send () normalmente bloquea, a menos que el zócalo se haya colocado en modo de E / S sin bloqueo.
Preguntas:
- ¿Esto significa que la llamada a
send()
siempre regresará inmediatamente si hay espacio en el buffer de envío del kernel? - ¿El comportamiento y el rendimiento de la llamada a
send()
idénticos para TCP y UDP? ¿Si no, porque no?
¿Esto significa que la llamada a send () siempre regresará inmediatamente si hay espacio en el buffer de envío del kernel?
¿No debería? El momento después del cual se envían los datos "se envía" se puede definir de manera diferente. Creo que este es un momento en que OS aceptó tus datos para la entrega en la pila. De lo contrario, es bastante difícil definirlo. ¿Es un momento en que los datos se transmiten a la memoria intermedia de la tarjeta de red? ¿O después del momento en que los datos se eliminan del búfer de la tarjeta de red?
¿Hay algún problema que necesites saber con seguridad o solo tienes curiosidad?
¿Esto significa que la llamada a send () siempre regresará inmediatamente si hay espacio en el buffer de envío del kernel?
Sí. Siempre que inmediatamente signifique que la memoria que proporcionó se copió en el búfer del kernel. Lo cual, en algunos casos extremos, puede no ser tan inmediato. Por ejemplo, si el puntero que ingresa desencadena un error de página que necesita extraer el búfer de un archivo mapeado en memoria o del intercambio, eso agregaría un retraso significativo a la devolución de la llamada.
¿El comportamiento y el rendimiento de la llamada a send () son idénticos para TCP y UDP? ¿Si no, porque no?
No exactamente. Las posibles diferencias de rendimiento dependen de la implementación del sistema operativo de la pila TCP / IP. En teoría, el socket UDP podría ser un poco más barato, ya que el sistema operativo necesita hacer menos cosas con él.
EDITAR: Por otro lado, dado que puede enviar mucha más información por llamada al sistema con TCP, generalmente el costo por byte puede ser mucho menor con TCP. Esto se puede mitigar con sendmmsg() en kernels recientes de Linux.
En cuanto al comportamiento, es casi idéntico.
Para los sockets de bloqueo, tanto TCP como UDP se bloquearán hasta que haya espacio en el búfer del kernel. Sin embargo, la distinción es que el socket UDP esperará hasta que todo su buffer pueda almacenarse en el buffer del kernel, mientras que el socket TCP puede decidir copiar solo un byte en el buffer del kernel (típicamente es más de un byte).
Si intenta enviar paquetes que son más grandes que 64kiB, es probable que un socket UDP falle sistemáticamente con EMSGSIZE . Esto se debe a que UDP, al ser un socket de datagramas , garantiza enviar todo el buffer como un único paquete IP (o tren de fragmentos de paquetes IP) o no enviarlo en absoluto.
Los sockets sin bloqueo se comportan de forma idéntica a las versiones de bloqueo con la única excepción de que en lugar de bloquear (en caso de que no haya suficiente espacio en el búfer del kernel), las llamadas fallan con EAGAIN (o EWOULDBLOCK ). Cuando esto sucede, es hora de volver a poner el socket en epoll / kqueue / select (o lo que sea que esté usando) para esperar a que vuelva a ser editable.
Como de costumbre cuando trabajas en POSIX, ten en cuenta que tu llamada puede fallar con EINTR (si la llamada fue interrumpida por una señal). En este caso, lo más probable es que desee volver a llamar a send()
.
El envío () volverá tan pronto como el kernel haya aceptado los datos. En caso de bloqueo de socket: El envío () se bloqueará si el kernel buffer no es lo suficientemente libre como para incorporar los datos proporcionados a la llamada a send ().
Sockets que no bloquean: send () no se bloqueará, pero fallará y devolverá -1 o puede devolver el número de bytes copiados parcialmente (dependiendo del espacio de búfer disponible). Establece el errno EWOULDBLOCK o EAGAIN. Esto significa que en ese momento de send (), el búfer no pudo ingresar todos los bytes y debe volver a intentarlo con la llamada select () para enviar () nuevamente los datos. O bien, podría poner un bucle con un sleep () y llamar a send (), pero debe tener en cuenta la cantidad de bytes realmente enviados y el número restante de bytes que se enviarán.
Si hay espacio en el búfer del núcleo, entonces send()
copia tantos bytes como sea posible en el búfer y sale inmediatamente, devolviendo la cantidad de bytes que se copiaron (que puede ser menor que la cantidad solicitada). Si no hay espacio en el buffer del kernel, entonces send()
bloques hasta que cualquiera de las salas esté disponible o que se agote el tiempo de espera (si hay una configurada).
Tu presunción es correcta. Si hay espacio en el buffer de envío del núcleo, el kernel copiará los datos en el buffer de send()
y se send()
.