2009-12-01 28 views
14

Sto provando a impostare un socket di blocco per il timeout dopo 16 ms di tentativo di recvfrom() su una porta. La piattaforma è Windows. Ho visto un sacco di esempi online e sembra davvero semplice, non riesco proprio a farlo funzionare. Qualsiasi aiuto sarebbe apprezzato!Imposta timeout per winsock recvfrom

#include <winsock2.h> 
#include <string> 

#pragma comment(lib, "ws2_32.lib") 

#define PORT_NUM 8001 

int main(void) 
{ 
    std::string localIP; 
    sockaddr_in localAddr; 
    sockaddr_in remoteAddr; 
    hostent* localhost; 
    char buffer[1024]; 
    WSADATA wsData; 

    int result = WSAStartup(MAKEWORD(2,2), &wsData); // winsock version 2 

    localhost = gethostbyname(""); 
    localIP = inet_ntoa(*(in_addr*)*localhost->h_addr_list); 

    localAddr.sin_family  = AF_INET; 
    localAddr.sin_port   = htons(PORT_NUM);    // Set Port Number 
    localAddr.sin_addr.s_addr = inet_addr(localIP.c_str()); // Set IP Address 

    int mHandle = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0); 

    if(mHandle == INVALID_SOCKET) 
    return 1; 


    if(bind(mHandle, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) 
    return 1; 

    timeval tv; 
    tv.tv_sec = 0; 
    tv.tv_usec = 1600; 

    // Set Timeout for recv call 
    if(setsockopt(mHandle, SOL_SOCKET, SO_RCVTIMEO, 
       reinterpret_cast<char*>(&tv), sizeof(timeval))) 
    return 1; 

    int length = sizeof(remoteAddr); 

    // <-- Blocks here forever 
    recvfrom(mHandle, buffer, 1024, 0, (SOCKADDR*)&remoteAddr, &length); 

    return 0; 
} 

/* I've also tried passing the time like so: 
int ms = 16; 

if(setsockopt(mHandle, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&ms), sizeof(int))) 
    return 1; */ 
+0

SO_RCVTIMEO non è molto portatile: quale piattaforma stai utilizzando? – Dipstick

+8

Probabilmente vuoi usare la funzione 'select' – laura

+0

Hey, grazie! Sì, ho esaminato il select e l'ho fatto funzionare! –

risposta

21

Ho esaminato la funzione di selezione e come laura ha detto che dovrei fare e farlo funzionare facilmente! Grazie!

fd_set fds ; 
int n ; 
struct timeval tv ; 

// Set up the file descriptor set. 
FD_ZERO(&fds) ; 
FD_SET(mHandle, &fds) ; 

// Set up the struct timeval for the timeout. 
tv.tv_sec = 10 ; 
tv.tv_usec = 0 ; 

// Wait until timeout or data received. 
n = select (mHandle, &fds, NULL, NULL, &tv) ; 
if (n == 0) 
{ 
    printf("Timeout..\n"); 
    return 0 ; 
} 
else if(n == -1) 
{ 
    printf("Error..\n"); 
    return 1; 
} 

int length = sizeof(remoteAddr); 

recvfrom(mHandle, buffer, 1024, 0, (SOCKADDR*)&remoteAddr, &length); 
+0

Quindi quello che capisco che tu mandi solo un pacchetto ogni volta che esegui la tua applicazione. Supponiamo che tu stia inviando un migliaio di pacchetti all'interno di un ciclo, questo codice potrebbe bloccarsi! Come risolverete questo problema? –

+0

Questa è la migliore risposta. L'altra risposta - SO_RCVTIMEO come DWORD - eseguirà il timeout della recvfrom, ma presenta alcuni problemi: 1) se il timeout si verifica, il socket non è più in uno stato valido. 2) Non sono riuscito a trovare documenti Windows che spiegassero come disattivare il timeout ... ma poiché il tuo socket sarà invalidato, credo che dovresti comunque crearne uno nuovo. –

1

Sto indovinando Windows dalla chiamata WSASocket(). Se è così, stai passando il timeout in modo errato.

MSDN dice che SO_RCVTIMEO accetta un parametro int che specifica il timeout in ms.

+0

Ho anche provato passandolo come il seguente: int ms = 16; if (setsockopt (mHandle, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast (& ms), sizeof (int))) ritorno 1; Ho ancora lo stesso risultato .. –

+0

Grazie, questo è proprio quello che mi serviva per un sistema di socket BSD che non accetta il SO_RCVTIMEO –

+0

Si dice anche "Se una chiamata di ricezione bloccante scade, la connessione è in uno stato indeterminato e deve essere chiuso Se il socket viene creato utilizzando la funzione WSASocket, il parametro dwFlags deve avere l'attributo WSA_FLAG_OVERLAPPED impostato per il timeout per funzionare correttamente, altrimenti il ​​timeout non ha effetto. ", quindi non sono sicuro se deve essere usato regolarmente ... – Marki555

13

ho provato facendola passare come il folloing

 int iTimeout = 1600; 
    iRet = setsockopt(pSapManager->m_cSocket, 
         SOL_SOCKET, 
         SO_RCVTIMEO, 
         /* 
         reinterpret_cast<char*>(&tv), 
         sizeof(timeval)); 
         */ 
         (const char *)&iTimeout, 
         sizeof(iTimeout)); 

ed eseguirlo !!

+0

Ciao, benvenuto in SO. Cerca di spiegare la tua risposta a parole, non solo nel codice.Sarà più facile per tutti imparare in questo modo. Inoltre, dal momento che stai rispondendo a una vecchia domanda, in che modo la tua risposta è diversa dalle altre? –