Was kann dazu führen, dass beim Befehl sock send () eine Ressource vorübergehend nicht verfügbar ist?

82

Was kann einen Resource temporarily unavailableFehler bei einem Socket- send()Befehl verursachen? Die Steckdose ist eingerichtet als AF_UNIX, SOCK_STREAM. Es funktioniert die meiste Zeit, aber gelegentlich wird dieser Fehler angezeigt. Das Empfangsende der Buchse scheint ordnungsgemäß zu funktionieren.

Ich weiß, dass dies nicht sehr detailliert ist, aber ich suche nur nach allgemeinen Ideen. Vielen Dank!

Giroy
quelle
Ist das verwandt? stackoverflow.com/questions/5737493/…
Paddy
3
Setzen Sie Ihren Socket auf O_NONBLOCK?
Deepankar Bajpeyi
Ich glaube nicht, dass es mit diesem Beitrag zusammenhängt. Meine Sockets sind SOCK_STREAM, von denen ich glaube, dass sie blockieren, was ich will.
Giroy
2
Ob ein Stream blockiert oder nicht blockiert, ist unabhängig davon, ob es sich um SOCK_STREAM oder SOCK_DGRAM handelt. Die Antwort dort ist relevant.
Barmar

Antworten:

95

"Resource temporarily unavailable"ist die entsprechende Fehlermeldung EAGAIN, was bedeutet, dass die Operation blockiert worden wäre, aber eine nicht blockierende Operation angefordert wurde. Denn send()das könnte folgende Ursachen haben:

  • explizite Kennzeichnung des Dateideskriptors als nicht blockierend mit fcntl(); oder
  • Weitergabe der MSG_DONTWAITFlagge an send(); oder
  • Festlegen eines Sendezeitlimits mit der SO_SNDTIMEOSocket-Option.
caf
quelle
Die Ursache für mein Problem war das Festlegen des Sendezeitlimits. Danke für deine Hilfe!
Giroy
@caf, In meinem Fall führte eine unterschiedliche Konfiguration der MTU-Größe auf zwei Seiten dazu, dass die sctp-Zuordnung Txqueue überlief, wenn eine hohe Rate an Paketaustausch stattfand. Wenn die MTU auf beiden Systemen gleich war, verschwand das Problem. Aber kann jemand bitte erklären, was der Grund für das Problem ist?
Codename_DJ
45

Das liegt daran, dass Sie einen non-blockingSocket verwenden und der Ausgabepuffer voll ist.

Von der send()Manpage

   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 ist der Fehlercode, der an "Ressource vorübergehend nicht verfügbar" gebunden ist.

Erwägen Sie die Verwendung select(), um eine bessere Kontrolle über dieses Verhalten zu erhalten

Davide Berra
quelle
@giroy aber nicht wirklich richtig ... eigentlich eine Blockierung Buchse, mitSO_SNDTIMEO
EML
Cool, aber wie können wir eine andere Verbindung verwenden, um das gleichzeitige Lesen in der Datenbank zu verwalten?
MUY Belgien
8

Lassen Sie mich ein Beispiel geben:

  1. Der Client stellt eine Verbindung zum Server her und sendet alle 1 Sekunde 1 MB Daten an den Server.

  2. Die Serverseite akzeptiert eine Verbindung und schläft dann 20 Sekunden lang, ohne eine Nachricht vom Client zu erhalten. Die tcp send bufferauf der Clientseite ist also voll.

Code auf der Clientseite:

#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 auf der Serverseite:

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

Starten Sie die Serverseite und dann die Clientseite.

Die Serverseite kann Folgendes ausgeben:

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

Geben Sie hier die Bildbeschreibung ein

Client-Seite kann ausgeben:

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 

Geben Sie hier die Bildbeschreibung ein

Sie können sehen, dass die Serverseite die Daten vom Client nicht abruft. Wenn also die Clientseite tcp buffervoll wird, Sie aber dennoch Daten senden, wird möglicherweise ein Resource temporarily unavailableFehler angezeigt .

Jayhello
quelle