2011-03-20 6 views
6

He leído la documentación al menos 10 veces y también he leído alrededor de 10 fragmentos de código y programas completos donde los sockets no bloqueantes se utilizan para enviar datos. El problema es que algunos de los tutoriales son para principiantes (Beejs f.i.) o son bastante descuidados en sus suposiciones; y aquellos que no son complicados son ejemplos de códigos especializados que no explican por qué hacen lo que hacen. Incluso la base de conocimiento de SO no cubre exhaustivamente toda la gama de comportamiento de send, en mi opinión. Lo que busco son detalles sobre f.e:¿Puede alguien darme una buena explicación del comportamiento de 'envío' para tomas sin bloqueo?

  • ¿Qué significa el código de retorno de 0 indican exactamente, y es digno de la comprobación errno en ese momento o si uno simplemente descartar la conexión sin más investigación?
  • ¿Obtener un valor de retorno negativo justifica el cierre de una conexión que no funciona bien o solo a menos que errno sea EWOULDBLOCK, EAGAIN o EINTR (... others)?
  • ¿Vale la pena consultar errno cuando el valor devuelto es > 0? Aparentemente, el valor indica la cantidad de datos "enviados" (entre comillas porque es realmente un proceso largo, correcto), pero dado que el socket no es bloqueado, significa que se puede emitir otra llamada de inmediato o, dependiendo de errno otra vez , uno debe esperar la próxima ocasión de envío (usando select/poll/epoll)?
  • Básicamente, ¿se verifica primero el valor de retorno y solo luego el valor errno? ¿O quizás send establece errno en cada llamada, independientemente del valor de retorno? Eso haría que la comprobación de errores fuera algo más fácil ...
  • Si se obtiene EINTR, ¿cuál sería un comportamiento bueno y sólido para un programa? Simplemente registre el estado y vuelva a intentarlo en la próxima ocasión de envío, como con EWOULDBLOCK y EAGAIN?
  • ¿Tiene un cheque por tanto EWOULDBLOCK y EAGAIN? ¿Podemos confiar en que ambos tengan el mismo valor o depende de la implementación?
  • ¿send devuelve EMSGSIZE para las tomas de corriente? Si no es así, entonces el tamaño de la memoria intermedia no es demasiado grande, ¿verdad?
  • ¿El valor de retorno puede ser igual a cualquiera de los códigos de error conocidos?

Si pudiera proporcionar un ejemplo de código de envío sólido sin bloqueo, sería absolutamente apreciado.

Respuesta

9

Una gran cantidad de preguntas aquí:

  • ¿Qué código de retorno de 0 indica exactamente, y es que vale la pena mirar errno continuación o sería mejor simplemente descartar la conexión sin más investigación?

En un sistema POSIX, enviar (2) nunca puede regresar 0 a menos que se llame con una longitud de arg 0. Compruebe la documentación para su sistema específico para asegurarse de que sigue la especificación POSIX

  • ¿Obtener un valor de retorno negativo justifica el cierre de una conexión que no está funcionando bien, o solo es así a menos que errno sea EWOULDBLOCK, EAGAIN o EINTR (... otros)?

No, un -1 valor de retorno (el único valor de retorno negativo posible) sólo significa que no hay datos fue enviado. Es necesario comprobar errno ver por qué - ver el (2) página de manual de envío para obtener una lista completa de todos los posibles valor errno y lo que significan

  • ¿Vale la pena comprobar errno cuando el valor de retorno es> 0? Aparentemente, el valor indica la cantidad de datos "enviados" (entre comillas porque realmente es un proceso largo, correcto), pero dado que el socket no es bloqueado, ¿significa que puede emitir otra llamada de inmediato o, dependiendo de errno nuevamente? , uno debe esperar la próxima ocasión de envío (usando select/poll/epoll)?

Si envíe devuelve éxito (> 0), entonces habrá errno sin cambios y contendrá todo lo que tenía antes (que es probablemente un error de alguna llamada al sistema anterior).

  • Básicamente, puede uno comprobar el valor devuelto por primera vez y sólo entonces el valor errno? O tal vez enviar conjuntos errno en cada llamada, devolver valor independientemente? Eso haría que la comprobación de errores algo más fácil ...

Comprobar el valor de retorno primero y luego errno si el valor de retorno es -1. Si realmente desea, puede asignar a errno a 0 antes de la llamada y luego comprobarlo después

  • Si uno se pone EINTR, lo que sería un buen comportamiento, robusto para un programa para tomar? Simplemente registre el estado y vuelva a intentarlo en la próxima ocasión de envío, como con EWOULDBLOCK y EAGAIN.

Bueno, la más simple es desactivar la interrupción de llamadas al sistema, en cuyo caso que no se consigue un EINTR. Tratarlo igual que EWOULDBLOCK/EAGAIN es bueno también.

  • ¿Uno cheque por tanto EWOULDBLOCK y EAGAIN? ¿Podemos confiar en que ambos tengan el mismo valor o depende de la implementación?

Depende de la implementación, aunque en general son los mismos. A veces hay rarezas con SysV vs BSD modos de emulación que podrían hacerlos diferentes y, o bien podría ocurrir

  • sí envía EMSGSIZE cambio de sockets de flujo? Si no es así, entonces el tamaño de la memoria intermedia no es demasiado grande, ¿verdad?

sockets de flujo no tienen mensajes atómicas y EMSGSIZE es sólo para los mensajes atómicos, así que no, sockets de flujo no pueden regresar EMSGSIZE

  • puede regresar propio valor sea igual a cualquiera de los códigos de error conocidos?

El único código de error es -1. El éxito es la cantidad de bytes escritos, por lo que si pudiera escribir 2^32-1 bytes en una máquina de 32 bits (o 2^64-1 en una máquina de 64 bits), sería un problema, pero no puede escriba tantos bytes (y generalmente obtendrá un EINVAL o EFAULT si lo intenta).

+1

send (2) puede devolver 0 si se pasa cero para 'len'. Para un protocolo de datagrama como UDP, esto incluso daría como resultado el envío de un paquete de cero bytes. Además, no se garantiza que errno no se modifique a través de una llamada exitosa a la biblioteca. – Anomie

+0

@Anomie: la primera parte es bastante cierta, pero por el segundo, POSIX garantiza que ciertas llamadas a la biblioteca no modificarán errno si no hay ningún error, y 'enviar' es una de esas llamadas. –

+0

¿Dónde ves eso? POSIX.1-2008 está en línea [aquí] (http://pubs.opengroup.org/onlinepubs/9699919799/). [La página en errno] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html) dice "La configuración de errno después de una llamada exitosa a una función no está especificada a menos que la descripción de esa función especifique que errno no se modificará ", y [la página para enviar] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) no parece decir tal cosa. – Anomie

3

Voy a tratar de responder a sus preguntas.

  • Un valor de retorno de 0 desde send indica que se han enviado 0 bytes. Un error se indica mediante un valor de retorno de -1. Si llamó al send con una longitud de 0, se debe esperar un retorno de 0. Mientras que un socket sin bloqueo debe devolver -1 con un errno de EAGAIN o EWOULDBLOCK si se bloquea, no me sorprendería excesivamente si alguna implementación devuelve 0 bytes escritos en su lugar.
  • EWOULDBLOCK, EAGAIN y EINTR son errores que debe volver a intentar, no cierre la conexión al recibir una de esas. Otros errores indican un problema que probablemente debería resultar en un cierre.
  • No, no revise errno después de una exitosa llamada a la biblioteca (a menos que la documentación indique específicamente que puede hacer esto por alguna razón, no estoy al tanto de ningún inconveniente que lo haga). Tenga en cuenta que errno puede no permanecer inalterado en una llamada exitosa a la biblioteca, ya que la llamada puede haber realizado otras llamadas que arrojaron errores que se esperaban y manejaban correctamente (por ejemplo, una llamada podría intentar registrar un archivo, esperando que no existiera; errno sería ENOENT a pesar de que no hubo un error real). Si el envío devuelve una breve redacción, puede volver a intentarlo (y posiblemente obtenga EWOULDBLOCK/EAGAIN) o podría esperar al próximo select.
  • Sí, primero revise el valor de retorno. errno no le dice nada útil si la llamada tuvo éxito.
  • En EINTR, podría intentarlo de nuevo inmediatamente o podría esperar la próxima vez a través del bucle select.
  • Debe comprobar EAGAIN y EWOULDBLOCK; Supongo que podría hacer #if EAGAIN == EWOULDBLOCK si el rendimiento es particularmente crítico (pero recuerde, el perfil luego optimice).
  • Todo dependería del protocolo subyacente, pero, en general, esperaría que un protocolo de transmisión no tuviera mensajes atómicos (a menos que tal vez al usar MSG_OOB). Para TCP, cualquier tamaño de búfer debe estar bien.
  • Ciertamente es posible que el valor de retorno sea igual a cualquiera de las constantes errno, pero no significa nada. Por ejemplo, en mi sistema si se escribieron 11 bytes, el valor de retorno sería igual a EAGAIN.

HTH.

2

Por EINTR y llamadas al sistema:

  • si está utilizando GLIBC, usted no necesita preocuparse de que, al menos en el contexto de las llamadas al sistema. Lo obtuve de Glibc FAQ, grep para "¿Por qué las señales ya no interrumpen las llamadas al sistema?"

  • si está utilizando LINUX, entonces probablemente no tenga que preocuparse por la extraña semántica de la llamada al sistema connect(), que David Madore enfada acerca de here. De lo contrario, esté preparado para un comportamiento distinto de lo habitual para la llamada asincrónica connect().

Cuestiones relacionadas