¿Por qué se supone que el envío puede regresar con menos de los datos solicitados transmitidos en un socket de bloqueo?

3 minutos de lectura

El método estándar para enviar datos en un socket de transmisión siempre ha sido llamar a enviar con una parte de los datos para escribir, verificar el valor devuelto para ver si se enviaron todos los datos y luego seguir llamando a enviar nuevamente hasta que se haya aceptado todo el mensaje.

Por ejemplo, este es un ejemplo simple de un esquema común:

int send_all(int sock, unsigned char *buffer, int len) {
  int nsent;

  while(len > 0) {
    nsent = send(sock, buffer, len, 0);
    if(nsent == -1) // error
      return -1;

    buffer += nsent;
    len -= nsent;
  }
  return 0; // ok, all data sent
}

Incluso la página de manual de BSD menciona que

…Si no hay espacio para mensajes disponible en el socket para contener el mensaje a transmitir, entonces envíe() normalmente bloquea

Lo que indica que debemos asumir que el envío puede devolverse sin enviar todos los datos. Ahora encuentro esto bastante roto, pero incluso W. Richard Stevens asume esto en su libro de referencia estándar sobre programación de redno en los capítulos iniciales, pero los ejemplos más avanzados utilizan su propia función writen (escribir todos los datos) en lugar de llamar a write.

Ahora considero que esto todavía está más o menos roto, ya que si el envío no puede transmitir todos los datos o aceptar los datos en el búfer subyacente y el socket está bloqueando, entonces el envío debe bloquearse y regresar cuando se haya aceptado toda la solicitud de envío. .

Quiero decir, en el ejemplo de código anterior, lo que sucederá si el envío devuelve con menos datos enviados es que se llamará nuevamente con una nueva solicitud. ¿Qué ha cambiado desde la última llamada? Como máximo, han pasado unos cientos de ciclos de CPU, por lo que el búfer aún está lleno. Si enviar ahora acepta los datos, ¿por qué no podría aceptarlos antes?

De lo contrario, terminaremos con un bucle ineficiente en el que intentaremos enviar datos en un socket que no puede aceptar datos y seguiremos intentándolo, ¿o no?

Por lo tanto, parece que la solución alternativa, si es necesaria, da como resultado un código muy ineficiente y, en esas circunstancias, se deben evitar los sockets de bloqueo y, en su lugar, se deben usar sockets que no bloqueen junto con select.

  • No se supone. La única forma en que esto puede suceder es con un modo de interrupción o sin bloqueo.

    – usuario207421

    17 de enero de 2020 a las 8:43

avatar de usuario
Nikolái Fetissov

Lo que falta en la descripción anterior es que, en Unix, las llamadas al sistema pueden interrumpirse con señales. Esa es exactamente la razón por la que bloquea send(2) podría devolver una cuenta corta.

  • ¿Eso no resultaría en EINTR? ¿O solo ocurrirá EINTR si la llamada se interrumpe antes de que se transmitan los datos como se describe en manpagez.com/man/2/send y si se han enviado datos, no se emite EINTR, sino que se devuelve la cantidad de datos enviados?

    – Ernelli

    11 de abril de 2010 a las 21:28

  • EINTR solo se devuelve si aún no se han transferido datos (y el controlador de señal no se instaló con el indicador SA_RESTART).

    – marca4o

    12 de abril de 2010 a las 15:22

  • ¿Es esa realmente la única razón? ¿Significaría eso que, si no instalo ningún controlador de señal, el bucle de envío sería innecesario? Eso me simplificaría bastante las cosas. Desafortunadamente, la documentación al respecto parece ser bastante escasa…

    – lxgr

    17 de enero de 2012 a las 18:49


  • @lxgr: SO_SNDTIMEO y posiblemente también close()ing el fd en otro subproceso puede causar escrituras breves. Además, las librerías de terceros pueden hacer cosas raras con las señales.

    – ninjalj

    24 de febrero de 2014 a las 13:27

  • ¡Ay! Me preguntaba por qué bloquearía y no cumplir con su pedido. ¿Con qué frecuencia ocurren las señales?

    – Pepiján

    10 de abril de 2014 a las 15:06

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad