¿Qué puede causar un "Recurso temporalmente no disponible" en el comando sock send ()

83

¿Qué puede causar un Resource temporarily unavailableerror en un send()comando de socket ? El enchufe está configurado como AF_UNIX, SOCK_STREAM. Funciona la mayor parte del tiempo, pero ocasionalmente recibe este error. El extremo receptor del enchufe parece funcionar correctamente.

Sé que esto no es muy detallado, pero solo estoy buscando ideas generales. ¡Gracias!

giroy
fuente
¿Está esto relacionado? stackoverflow.com/questions/5737493/…
paddy
3
¿Está configurando su enchufe en O_NONBLOCK?
Deepankar Bajpeyi
No creo que esté relacionado con esa publicación. Mis sockets son SOCK_STREAM que creo que están bloqueando, que es lo que quiero.
giroy
2
Si una transmisión es bloqueante o no bloqueante es independiente de si es SOCK_STREAM o SOCK_DGRAM. La respuesta es relevante.
Barmar

Respuestas:

95

"Resource temporarily unavailable"es el mensaje de error correspondiente a EAGAIN, lo que significa que la operación se habría bloqueado pero se solicitó la operación sin bloqueo. Porque send(), eso podría deberse a cualquiera de:

  • marcar explícitamente el descriptor de archivo como no bloqueante con fcntl(); o
  • pasar la MSG_DONTWAITbandera a send(); o
  • establecer un tiempo de espera de envío con la SO_SNDTIMEOopción de socket.
coste y flete
fuente
La causa de mi problema fue establecer el tiempo de espera de envío. ¡Gracias por tu ayuda!
giroy
@caf, en mi caso, la configuración de tamaño de MTU diferente en dos lados estaba causando que la asociación sctp Txqueue se desbordara cuando se estaba produciendo una alta tasa de intercambio de paquetes. Hacer que la MTU sea la misma en ambos sistemas hizo que el problema desapareciera. Pero, ¿alguien puede explicar cuál es la razón del problema?
Codename_DJ
45

Eso es porque estás usando un non-blockingsocket y el búfer de salida está lleno.

Desde la send()página del manual

   When the message does not fit into  the  send  buffer  of  the  socket,
   send() normally blocks, unless the socket has been placed in non-block-
   ing I/O mode.  In non-blocking mode it  would  return  EAGAIN  in  this
   case.  

EAGAIN es el código de error vinculado a "Recurso temporalmente no disponible"

Considere usar select()para obtener un mejor control de estos comportamientos

Davide Berra
fuente
@giroy: pero no realmente correcto ... en realidad un socket de bloqueo , conSO_SNDTIMEO
EML
Genial, pero ¿cómo podemos usar otra conexión para administrar la lectura simultánea en la base de datos?
MUY Bélgica
8

Déjame darte un ejemplo:

  1. el cliente se conecta al servidor y envía 1 MB de datos al servidor cada segundo.

  2. El lado del servidor acepta una conexión y luego duerme 20 segundos, sin recibir el mensaje tcp send bufferdel cliente. Por lo tanto, el lado del cliente estará lleno.

Código en el lado del cliente:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#define exit_if(r, ...)                                                                          \
    if (r) {                                                                                     \
        printf(__VA_ARGS__);                                                                     \
        printf("%s:%d error no: %d error msg %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
        exit(1);                                                                                 \
    }

void setNonBlock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    exit_if(flags < 0, "fcntl failed");
    int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    exit_if(r < 0, "fcntl failed");
}

void test_full_sock_buf_1(){
    short port = 8000;
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;


    int fd = socket(AF_INET, SOCK_STREAM, 0);
    exit_if(fd<0, "create socket error");

    int ret = connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr));
    exit_if(ret<0, "connect to server error");
    setNonBlock(fd);

    printf("connect to server success");

    const int LEN = 1024 * 1000;
    char msg[LEN];  // 1MB data
    memset(msg, 'a', LEN);

    for (int i = 0; i < 1000; ++i) {
        int len = send(fd, msg, LEN, 0);
        printf("send: %d, erron: %d, %s \n", len, errno, strerror(errno));
        sleep(1);
    }

}

int main(){
    test_full_sock_buf_1();

    return 0;
}

Código en el lado del servidor:

    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <stdio.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    #define exit_if(r, ...)                                                                          \
        if (r) {                                                                                     \
            printf(__VA_ARGS__);                                                                     \
            printf("%s:%d error no: %d error msg %s\n", __FILE__, __LINE__, errno, strerror(errno)); \
            exit(1);                                                                                 \
        }
void test_full_sock_buf_1(){

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    exit_if(listenfd<0, "create socket error");

    short port = 8000;
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    int r = ::bind(listenfd, (struct sockaddr *) &addr, sizeof(struct sockaddr));
    exit_if(r<0, "bind socket error");

    r = listen(listenfd, 100);
    exit_if(r<0, "listen socket error");

    struct sockaddr_in raddr;
    socklen_t rsz = sizeof(raddr);
    int cfd = accept(listenfd, (struct sockaddr *) &raddr, &rsz);
    exit_if(cfd<0, "accept socket error");

    sockaddr_in peer;
    socklen_t alen = sizeof(peer);
    getpeername(cfd, (sockaddr *) &peer, &alen);

    printf("accept a connection from %s:%d\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));

    printf("but now I will sleep 15 second, then exit");
    sleep(15);
}

Inicie el lado del servidor, luego inicie el lado del cliente.

el lado del servidor puede generar:

accept a connection from 127.0.0.1:35764
but now I will sleep 15 second, then exit
Process finished with exit code 0

ingrese la descripción de la imagen aquí

el lado del cliente puede generar:

connect to server successsend: 1024000, erron: 0, Success 
send: 1024000, erron: 0, Success 
send: 1024000, erron: 0, Success 
send: 552190, erron: 0, Success 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 11, Resource temporarily unavailable 
send: -1, erron: 104, Connection reset by peer 
send: -1, erron: 32, Broken pipe 
send: -1, erron: 32, Broken pipe 
send: -1, erron: 32, Broken pipe 
send: -1, erron: 32, Broken pipe 
send: -1, erron: 32, Broken pipe 

ingrese la descripción de la imagen aquí

Como puede ver, como el lado del servidor no recupera los datos del cliente, cuando el lado del cliente tcp bufferse llena, pero aún envía datos, puede obtener un Resource temporarily unavailableerror.

Jayhello
fuente