Linux: czy istnieje odczyt lub odzyskanie z gniazda z przekroczeniem limitu czasu?

106

Jak mogę spróbować odczytać dane z gniazda z przekroczeniem limitu czasu? Wiem, select, pselect, poll, ma pole limitu czasu, ale ich użycie wyłącza "tcp fast-path" w stosie tcp reno.

Jedyny pomysł, jaki mam, to użycie recv (fd, ..., MSG_DONTWAIT) w pętli

osgx
źródło
Istnieje również opcja korzystania z wątków :), ale sygnały wątku są nadal potrzebne
osgx

Odpowiedzi:

189

Możesz użyć funkcji setsockopt, aby ustawić limit czasu dla operacji odbierania:

SO_RCVTIMEO

Ustawia wartość limitu czasu, która określa maksymalny czas, przez jaki funkcja wejściowa oczekuje do zakończenia. Akceptuje strukturę czasu z liczbą sekund i mikrosekund określającą limit czasu oczekiwania na zakończenie operacji wejściowej. Jeśli operacja odbioru została zablokowana przez tak długi czas bez otrzymywania dodatkowych danych, powinna powrócić z częściowym zliczaniem lub errno ustawionym na [EAGAIN] lub [EWOULDBLOCK], jeśli żadne dane nie są odbierane. Wartością domyślną dla tej opcji jest zero, co oznacza, że ​​operacja odbierania nie powinna przekraczać limitu czasu. Ta opcja ma strukturę chronologiczną. Zauważ, że nie wszystkie implementacje pozwalają na ustawienie tej opcji.

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

Podobno w systemie Windows należy to zrobić przed wywołaniem bind. Eksperymentalnie potwierdziłem, że można to zrobić przed lub po bindw systemie Linux i OS X.

Robert S. Barnes
źródło
1
Ta odpowiedź uratowała mi tyłek. Utknąłem we wdrażaniu tego zawiłego „wybierz” bzdury, ale bez sukcesu. To zadziałało natychmiast, o wiele prostsze.
MiloDC
Właśnie dlatego limit czasu w systemie Windows nie działa, ponieważ używam kodu dla systemu Linux. Dzięki. Jeśli system Windows nie jest używany, struct timeval tv;czy oznacza to, że funkcja select () też nie będzie działać? Próbowałem przenieść mój kod select () do systemu Windows i natychmiast przekroczył limit czasu, wygląda na to, że ignoruje wartość, którą ustawiam w timeval.
kuchi
1
Ustawiłem wartość limitu czasu na 5 sekund. Dlaczego każdy cykl odczytu zawsze zajmuje 5 sekund, niezależnie od tego, czy przychodzą dane, czy nie?
Han
działa to również w systemie Windows nawet po operacji wiązania. wypróbowany w
systemie
1
@ user463035818 Ta odpowiedź twierdzi, że nie powinno.
Tomeamis
22

Oto prosty kod dodający limit czasu do recvfunkcji pollw języku 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;
}
Abdessamad Doughri
źródło
nie działałoby to dokładnie tak, jak oczekiwano. pollbędzie czekał na odebranie co najmniej jednego bajtu lub przekroczenia limitu czasu, podczas gdy podczas wywoływania recvfunkcji będzie czekał na sizeof(buf)bajty, powodując jej ponowne zablokowanie, jeśli ten licznik jeszcze nie nadszedł, ale tym razem bez przekroczenia limitu czasu.
LoPiTaL
0

// działa również po operacji wiązania dla WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
cahit beyaz
źródło
-1

Zainstaluj moduł obsługi dla SIGALRM, a następnie użyj alarm()lub ualarm()przed zwykłym blokowaniem recv(). Jeśli alarm się recv()włączy, zwróci błąd z errnoustawieniem na EINTR.

kawiarnia
źródło
8
alarmy (i sygnały) to niewłaściwa droga do tego zadania. Jeśli chcę używać szybkiej ścieżki TCP, potrzebuję minimalnego opóźnienia. Sygnały są powolne.
osgx
2
@osgx Sygnał występuje tylko w przypadku przekroczenia limitu czasu.
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));

WINDOWS

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

UWAGA : To ustawienie zostało wprowadzone przed bind()wywołaniem funkcji dla prawidłowego uruchomienia

vivek
źródło
4
Na to pytanie udzielono już odpowiedzi wiele lat temu. Jaką nową wartość wnosi Twoje rozwiązanie?
Maciej Jureczko
To ustawienie umieściłeś przed wywołaniem funkcji bind () w celu prawidłowego uruchomienia tej części nie ma wzmianki w ans
vivek