GNU/Linux >> Tutoriels Linux >  >> Linux

Qu'est-ce qui peut provoquer une "Ressource temporairement indisponible" sur la commande sock send()

"Resource temporarily unavailable" est le message d'erreur correspondant à EAGAIN , ce qui signifie que l'opération aurait été bloquée mais qu'une opération non bloquante a été demandée. Pour send() , cela peut être dû à l'une des raisons suivantes :

  • marquage explicite du descripteur de fichier comme non bloquant avec fcntl(); ou
  • réussir le MSG_DONTWAIT drapeau à send(); ou
  • définir un délai d'envoi avec le SO_SNDTIMEO option de prise.

Prenons un exemple :

  1. le client se connecte au serveur et envoie 1 Mo de données au serveur toutes les 1 seconde.

  2. le côté serveur accepte une connexion, puis dort 20 secondes, sans recevoir de message du client. Ainsi, le tcp send buffer du côté client sera plein.

Code côté client :

#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;
}

Code côté serveur :

    #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);
}

Démarrez côté serveur, puis démarrez côté client.

le côté serveur peut afficher :

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

le côté client peut afficher :

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 

Vous pouvez voir, comme le côté serveur ne reçoit pas les données du client, donc quand le côté client tcp buffer être plein, mais vous envoyez toujours des données, vous pouvez donc obtenir Resource temporarily unavailable erreur.


C'est parce que vous utilisez un non-blocking socket et le tampon de sortie est plein.

À partir du send() page de manuel

   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.  

ENCORE est le code d'erreur lié à "Ressource temporairement indisponible"

Envisagez d'utiliser select() pour mieux contrôler ces comportements


Linux
  1. La commande Docker ne peut pas se connecter au démon Docker

  2. Que faire lorsque Ctrl + C ne peut pas tuer un processus ?

  3. Qu'est-ce qui pourrait amener la commande file sous Linux à signaler un fichier texte en tant que données binaires ?

  4. À quoi sert Linux test -a command test ?

  5. Quel est le but de cd ` (backtick) ?

Qu'est-ce qu'un dotfile shell peut faire pour vous

Qu'est-ce que la commande Linux Watch + Exemples

Qu'est-ce que mon invite de commande Linux me dit?

Qu'est-ce que la commande kill sous Linux ?

Qu'est-ce qu'une commande pour trouver la priorité d'un processus sous Linux ?

La création de threads échoue avec "Ressource temporairement indisponible" avec le noyau 4.3