2015-05-22 32 views
9

Odczytuję z gniazda przy użyciu funkcji recv. Mam problem, gdy brak danych do czytania. Mój program się zatrzymuje. Zauważyłem, że mogę ustawić limit czasu przy użyciu funkcji select. Ale wygląda na to, że timeout wpływa na samą funkcję wyboru, a recv, która przechodzi po wybraniu, wciąż czeka w trybie ciągłym.Ustawianie limitu czasu na funkcję recv

fd_set set; 
struct timeval timeout; 
FD_ZERO(&set); /* clear the set */ 
FD_SET(s, &set); /* add our file descriptor to the set */ 
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC; 
timeout.tv_usec = 0; 
int rv = select(s, &set, NULL, NULL, &timeout); 
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR) 
     { 
     ... 
     } 

Jak poprosić o powrót funkcji po pewnym czasie oczekiwania?

Odpowiedz

6

Należy sprawdzić wartość zwracaną select. select powróci 0 wyciszony przypadku wygasł, więc należy sprawdzić błąd i wywołać recv tylko jeśli select powrócił pozytywną wartość:

Po pomyślnym zakończeniu, select() i pselect() zwracają liczbę deskryptorów w trzy zwrócone zbiory deskryptorów (czyli łączna liczba bitów ustawionych w readfds, writefds, exceptfds), które mogą wynosić zero, jeśli limit czasu upłynie zanim wydarzy się coś ciekawego.

int rv = select(s + 1, &set, NULL, NULL, &timeout); 
if (rv == SOCKET_ERROR) 
{ 
    // select error... 
} 
else if (rv == 0) 
{ 
    // timeout, socket does not have anything to read 
} 
else 
{ 
    // socket has something to read 
    recv_size = recv(s, rx_tmp, bufSize, 0); 
    if (recv_size == SOCKET_ERROR) 
    { 
     // read failed... 
    } 
    else if (recv_size == 0) 
    { 
     // peer disconnected... 
    } 
    else 
    { 
     // read successful... 
    } 
} 
+0

Przykład ten kod ma błąd ... linię który czyta: 'int rv = select (s, & set, NULL, NULL i timeout);' powinien przeczytać 'int rv = select (s + 1, & set, NULL, NULL i timeout);' – drbobdugan

+0

Dziękuję bardzo, ty rzeczywiście ma rację! 'nfds' jest deskryptorem pliku o najwyższym numerze + 1. –

21

Innym sposobem, aby ustawić limit czasu na samej recv() bez użycia select() jest użycie setsockopt() ustawić SO_RCVTIMEO opcję gniazda za (na platformach, które obsługują).

W Windows, kod wyglądałby następująco:

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

//... 

recv_size = recv(s, rx_tmp, bufSize, 0); 
if (recv_size == SOCKET_ERROR) 
{ 
    if (WSAGetLastError() != WSAETIMEDOUT) 
     //... 
} 

na innych platformach, kod będzie wyglądać tak zamiast:

struct timeval timeout; 
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC; 
timeout.tv_usec = 0; 
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 

//... 

recv_size = recv(s, rx_tmp, bufSize, 0); 
if (recv_size == -1) 
{ 
    if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) 
     //... 
} 
+1

FYI To rozwiązanie działa tylko na platformach Windows, nie może być używane na platformach Unix/Linux/OSX. – drbobdugan

+1

@drbobdugan: Windows nie jest jedyną platformą obsługującą 'SO_RCVTIMEO', chociaż na innych platformach parametr wejściowy ma zwykle strukturę' timeval'. [Linux implementuje 'SO_RCVTIMEO' (i' SO_SNDTIMEO')] (http://man7.org/linux/man-pages/man7/socket.7.html). [Tak samo działa OSX] (https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setsockopt.2.html). –

+0

Twoje rozwiązanie nie będzie działać na platformie Linux/Unix/OSX, ponieważ zawiera fragmenty kodu, takie jak DWORD, WSAGetLastError(), które są artefaktami systemu Windows. – drbobdugan