Linux: Gibt es einen Lese- oder Rückruf vom Socket mit Timeout?

105

Wie kann ich versuchen, Daten aus dem Socket mit Timeout zu lesen? Ich weiß, auswählen, auswählen, abfragen, hat ein Timeout-Feld, aber wenn ich sie verwende, wird "tcp fast-path" im tcp reno-Stapel deaktiviert.

Die einzige Idee, die ich habe, ist, recv (fd, ..., MSG_DONTWAIT) in einer Schleife zu verwenden

osgx
quelle
Es gibt auch eine Option zur Verwendung von Threads :), aber Thread-Signale werden weiterhin benötigt
osgx

Antworten:

189

Sie können die Verwendung setsockopt Funktion ein Timeout einstellen auf Operationen erhalten:

SO_RCVTIMEO

Legt den Timeout-Wert fest, der die maximale Wartezeit einer Eingabefunktion bis zum Abschluss angibt. Es akzeptiert eine Zeitstruktur mit der Anzahl von Sekunden und Mikrosekunden, die die Grenze für die Wartezeit bis zum Abschluss eines Eingabevorgangs angibt. Wenn eine Empfangsoperation für so lange Zeit blockiert wurde, ohne zusätzliche Daten zu empfangen, wird sie mit einer Teilzählung oder einer Fehlernummer zurückgegeben, die auf [EAGAIN] oder [EWOULDBLOCK] gesetzt ist, wenn keine Daten empfangen werden. Der Standardwert für diese Option ist Null. Dies gibt an, dass für eine Empfangsoperation keine Zeitüberschreitung auftreten soll. Diese Option hat eine zeitliche Struktur. Beachten Sie, dass diese Option nicht in allen Implementierungen festgelegt werden kann.

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

Berichten zufolge sollte dies unter Windows vor dem Aufruf erfolgen bind. Ich habe experimentell bestätigt, dass dies entweder vor oder nach bindLinux und OS X möglich ist.

Robert S. Barnes
quelle
1
Diese Antwort hat meinen Arsch gerettet. Ich war festgefahren, diesen verschlungenen "ausgewählten" Mist umzusetzen, ohne Erfolg. Das hat sofort funktioniert, viel einfacher.
MiloDC
Deshalb funktioniert Timeout unter Windows nicht, weil ich den Code für Linux verwende. Vielen Dank. Wenn Windows nicht verwendet struct timeval tv;wird, bedeutet dies, dass select () auch nicht funktioniert? Ich habe versucht, meinen select () - Code nach Windows zu portieren, und es tritt sofort eine Zeitüberschreitung auf. Es sieht so aus, als würde der Wert, den ich zum Zeitpunkt eingestellt habe, ignoriert.
Kuchi
1
Ich habe den Timeout-Wert auf 5 Sekunden eingestellt. Warum dauert es für jeden Lesezyklus immer 5 Sekunden, unabhängig davon, ob Daten eingehen oder nicht?
Han
Dies funktioniert auch unter Windows auch nach dem Bindevorgang. versucht auf Windows 10
Cahit Beyaz
1
@ user463035818 Diese Antwort behauptet, es sollte nicht.
Tomeamis
22

Hier ist ein einfacher Code, mit dem Sie Ihrer recvFunktion pollin C eine Zeitüberschreitung hinzufügen können :

struct pollfd fd;
int ret;

fd.fd = mySocket; // your socket handler 
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
    case -1:
        // Error
        break;
    case 0:
        // Timeout 
        break;
    default:
        recv(mySocket,buf,sizeof(buf), 0); // get your data
        break;
}
Abdessamad Doughri
quelle
Dies würde nicht genau wie erwartet funktionieren. pollwartet auf den Empfang von mindestens einem Byte oder Timeout, während beim Aufrufen der recvFunktion auf sizeof(buf)Bytes gewartet wird , wodurch diese erneut blockiert wird, wenn diese Anzahl noch nicht eingetroffen ist, diesmal jedoch ohne Timeout.
LoPiTaL
0

// funktioniert auch nach dem Bindevorgang für WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
cahit beyaz
quelle
-1

Installieren Sie einen Handler für SIGALRM, dann verwenden Sie alarm()oder ualarm()vor einer regulären Blockierung recv(). Wenn der Alarm ausgelöst wird, recv()wird ein Fehler mit der errnoEinstellung auf zurückgegeben EINTR.

caf
quelle
8
Alarme (und Signale) sind der falsche Weg zu dieser Aufgabe. Wenn ich einen schnellen TCP-Pfad verwenden möchte, benötige ich eine minimale Latenz. Die Signale sind langsam.
Osgx
2
@osgx Das Signal tritt nur auf, wenn eine Zeitüberschreitung vorliegt.
David Schwartz
-4

LINUX

struct timeval tv;
tv.tv_sec = 30;        // 30 Secs Timeout
tv.tv_usec = 0;        // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));

FENSTER

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

HINWEIS : Sie haben diese Einstellung vor dem bind()Funktionsaufruf für einen ordnungsgemäßen Lauf festgelegt

vivek
quelle
4
Diese Frage wurde bereits vor Jahren beantwortet. Welchen neuen Wert bringt Ihre Lösung?
Maciej Jureczko
Sie haben für die richtigen Lauf diese Einstellung vor bind () Funktionsaufruf setzen dieser Teil erwähnen ist nicht in Am
vivek