servidor - socket example java
¿Se puede enviar() en un retorno de socket TCP>=0 y<longitud? (5)
De acuerdo con la especificación de Posix y todas las páginas man 2 send que he visto en 30 años, sí,
send()
puede devolver cualquier valor> 0 y <=length
. Tenga en cuenta que no puede devolver cero.De acuerdo con una discusión hace unos años sobre news: comp.protocols.tcp-ip donde están todos los implementadores de TCP, un
send()
bloqueo no regresará hasta que haya transferido todos los datos al buffer de envío de socket: en otros palabras, el valor de retorno es -1 olength.
Se acordó que esto era cierto para todas las implementaciones conocidas y también parawrite(),
writev()
,sendmsg()
,writev()
,
He visto una serie de preguntas sobre el send()
que tratan el protocolo subyacente. Soy plenamente consciente de que para TCP cualquier mensaje puede dividirse en partes a medida que se envía y no hay garantía de que el receptor reciba el mensaje en una sola operación atómica. En esta pregunta, me refiero únicamente al comportamiento de la llamada al sistema send()
ya que interactúa con la capa de red del sistema local.
De acuerdo con el estándar POSIX y la documentación de send()
que he leído, la longitud del mensaje a enviar se especifica mediante el argumento de longitud. Tenga en cuenta que: send()
envía un mensaje, de longitud . Promover:
Si no hay espacio disponible en el socket de envío para retener el mensaje a transmitir, y el descriptor del archivo de socket no tiene configurado
O_NONBLOCK
,send()
se bloqueará hasta que haya espacio disponible. Si no hay espacio disponible en el socket de envío para retener el mensaje a transmitir, y el descriptor del archivo de socket tieneO_NONBLOCK
configurado,send()
fallará.
No veo ninguna posibilidad en esta definición para que send () devuelva un valor que no sea -1
(lo que significa que no hay datos en cola en el núcleo para ser transmitidos) o la longitud , lo que significa que todo el mensaje está en cola en el núcleo para ser transmitido Es decir, me parece que send()
debe ser atómico con respecto a poner en cola el mensaje localmente para su entrega en el kernel .
- Si hay suficiente espacio en la cola de sockets en el kernel para todo el mensaje y no se produce ninguna señal (caso normal), se copia y devuelve la longitud .
- Si se produce una señal durante el
send()
, entonces debe devolver-1
. Obviamente no podemos tener una parte del mensaje en la cola en este caso, ya que no sabemos cuánto se envió. Así que nada puede ser enviado en esta situación. - Si no hay suficiente espacio en la cola del socket en el kernel para todo el mensaje y el socket está bloqueando, entonces, de acuerdo con la declaración anterior, el bloque
send()
debe bloquear hasta que haya espacio disponible. Entonces el mensaje se pondrá en cola ysend()
devuelve la longitud . - Si no hay suficiente espacio en la cola de sockets en el kernel para el mensaje completo y el socket no está bloqueado, entonces
send()
debe fallar (retorno-1
) yerrno
se configurará comoEAGAIN
oEWOULDBLOCK
. Nuevamente, dado que devolvemos-1
, está claro que en esta situación no se puede poner en cola ninguna parte del mensaje.
¿Me estoy perdiendo de algo? ¿Es posible que send () devuelva un valor que es >=0 && <length
? ¿En qué situación? ¿Qué pasa con los sistemas que no son POSIX / UNIX? ¿La implementación de Windows send()
es compatible con esto?
Es posible que send()
devuelva un value >= 0 && < length
. Esto podría suceder si el búfer de envío tiene menos espacio que la longitud del mensaje en una llamada a send()
. De manera similar, si el tamaño de la ventana del receptor actual que conoce el remitente es menor que la longitud del mensaje, solo se puede enviar parte del mensaje. Como anécdota, he visto que esto sucede en Linux a través de una conexión localhost cuando el proceso de recepción demoró en descargar los datos que estaba recibiendo de su búfer de recepción.
Mi sensación es que la experiencia real variará un poco según la implementación. Desde este enlace de Microsoft , está claro que puede ocurrir un valor de retorno sin error menor que la longitud.
También es posible obtener un valor de retorno de cero (de nuevo, al menos con algunas implementaciones) si se envía un mensaje de longitud cero.
Esta respuesta se basa en mi experiencia, y se basa en esta respuesta SO particularmente.
Edición: a partir de esta respuesta y sus comentarios, evidentemente, un error EINTR
solo puede producirse si la interrupción se produce antes de que se envíe cualquier dato, lo que sería otra forma posible de obtener dicho valor de retorno.
Para aclarar un poco, donde dice:
Bloqueará hasta que haya espacio disponible.
Hay varias maneras de despertar de ese bloqueo / sueño:
- Se dispone de suficiente espacio.
- Una señal interrumpe la operación de bloqueo actual.
-
SO_SNDTIMEO
está configurado para el socket y el tiempo de espera expira. - Otros, por ejemplo, el zócalo está cerrado en otro hilo.
Entonces las cosas terminan así:
- Si hay suficiente espacio en la cola de socket en el núcleo para el mensaje completo y no se produce ninguna señal (caso normal), se copia y devuelve la longitud.
-
Si se produce una señal durante el envío (), entonces debe devolver -1.Obviamente no podemos tener una parte del mensaje en la cola en este caso, ya que no sabemos cuánto se envió.Así que nada puede ser enviado en esta situación. - Si no hay suficiente espacio en la cola del socket en el kernel para todo el mensaje y el socket está bloqueando, entonces, de acuerdo con la declaración anterior, el bloque () debe bloquear hasta que haya espacio disponible.
Entonces el mensaje se pondrá en cola y send () devuelve la longitud.Luego, elsend()
puede ser interrumpido por una señal, el tiempo de espera de envío puede transcurrir, ... causando un envío corto / escritura parcial. Las implementaciones razonables devolverán -1 y estableceránerrno
en un valor adecuado si no se ha copiado nada en el búfer de envío. - Si no hay suficiente espacio en la cola de sockets en el kernel para el mensaje completo y el socket no está bloqueado, entonces send () debe fallar (retorno -1) y errno se configurará como EAGAIN o EWOULDBLOCK. Nuevamente, dado que devolvemos -1, está claro que en esta situación no se puede poner en cola ninguna parte del mensaje.
Sé cómo funciona la cosa en Linux, con la biblioteca C de GNU. El punto 4 de su pregunta se lee de manera diferente en este caso. Si establece el indicador O_NONBLOCK
para el descriptor de archivo, y si no es posible poner en cola el mensaje completo en el kernel de forma atómica, send()
devuelve el número de bytes realmente enviados (puede estar entre 1 y longitud), y errno
es establecer en EWOULDBLOCK
.
(Con un descriptor de archivo trabajando en el modo de bloqueo, send()
bloquearía).
Su punto 2 está sobre simplificado. La condición normal bajo la cual el send
devuelve un valor mayor que cero pero menor que la longitud (tenga en cuenta que, como han dicho otros, nunca puede devolver cero excepto cuando el argumento de la longitud es cero) es cuando el mensaje es lo suficientemente largo como para provocar un bloqueo. y una señal de interrupción llega después de que ya se haya enviado algún contenido. En este caso, el send
no puede fallar con EINTR
(porque esto evitaría que la aplicación supiera que ya había enviado algunos datos con éxito) y no puede volver a bloquearse (ya que la señal está interrumpiendo, y todo el punto de eso es salir de bloqueo), por lo que tiene que devolver el número de bytes ya enviados, que es menor que la longitud total solicitada.