Linux: c'è una lettura o una ricezione dal socket con timeout?


105

Come posso provare a leggere i dati dal socket con timeout? Lo so, select, pselect, poll, ha un campo timeout, ma il loro utilizzo disabilita "tcp fast-path" nello stack tcp reno.

L'unica idea che ho è usare recv (fd, ..., MSG_DONTWAIT) in un ciclo


C'è anche un'opzione per usare i thread :) ma i segnali dei thread sono ancora necessari
osgx

Risposte:


189

È possibile utilizzare la funzione setsockopt per impostare un timeout sulle operazioni di ricezione:

SO_RCVTIMEO

Imposta il valore di timeout che specifica la quantità massima di tempo che una funzione di input attende fino al suo completamento. Accetta una struttura temporale con il numero di secondi e microsecondi che specifica il limite di tempo di attesa per il completamento di un'operazione di input. Se un'operazione di ricezione si è bloccata per così tanto tempo senza ricevere dati aggiuntivi, tornerà con un conteggio parziale o errno impostato su [EAGAIN] o [EWOULDBLOCK] se non vengono ricevuti dati. Il valore predefinito per questa opzione è zero, che indica che un'operazione di ricezione non deve scadere. Questa opzione ha una struttura temporale. Notare che non tutte le implementazioni consentono di impostare questa opzione.

// 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);

Secondo quanto riferito su Windows, questo dovrebbe essere fatto prima di chiamare bind. Ho verificato per esperimento che può essere fatto prima o dopo bindsu Linux e OS X.


1
Questa risposta mi ha salvato il culo. Ero bloccato nell'implementare quella contorta "selezione" di schifezze, senza successo. Questo ha funzionato immediatamente, molto più semplice.
MiloDC

Questo è il motivo per cui il timeout su Windows non funziona perché sto usando il codice per Linux. Grazie. Se Windows non è in uso struct timeval tv;, significa che anche select () non funzionerà? Ho provato a portare il mio codice select () su Windows e si è appena verificato un timeout immediatamente, sembra che stia ignorando il valore che sto impostando al momento.
kuchi

1
Ho impostato il valore di timeout a 5 secondi. Perché ci vogliono sempre 5 secondi per ogni ciclo di lettura indipendentemente dalla presenza di dati in arrivo o meno?
Han

funziona anche su Windows anche dopo l'operazione di binding. provato su Windows 10
Cahit Beyaz

1
@ user463035818 Questa risposta afferma che non dovrebbe.
Tomeamis

22

Ecco un semplice codice per aggiungere un timeout alla tua recvfunzione utilizzando pollin C:

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

questo non funzionerebbe esattamente come previsto. pollattenderà di ricevere almeno un byte o timeout, mentre chiamando la recvfunzione attenderà i sizeof(buf)byte, provocando il blocco di nuovo se questo conteggio non è ancora arrivato, ma questa volta senza avere timeout.
LoPiTaL

0

// funziona anche dopo l'operazione di bind per WINDOWS

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

-1

Installa un gestore per SIGALRM, quindi usa alarm()o ualarm()prima di un blocco regolare recv(). Se l'allarme si spegne, recv()restituirà un errore con errnoimpostato su EINTR.


8
allarmi (e segnali) sono il modo sbagliato per questo compito. Se voglio usare il percorso veloce tcp, ho bisogno di una latenza minima. I segnali sono lenti.
osgx

2
@osgx Il segnale si verifica solo in caso di timeout.
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));

FINESTRE

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

NOTA : questa impostazione è stata inserita prima della bind()chiamata di funzione per una corretta esecuzione


4
Questa domanda ha già ricevuto risposta anni fa. Quale nuovo valore porta la tua soluzione?
Maciej Jureczko

Hai inserito questa impostazione prima della chiamata alla funzione bind () per eseguire correttamente questa parte non è menzionata in ans
vivek
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.