39
votes

I am trying to set a 100ms timeout on a UDP Socket. I am using C. I have posted relavent pieces of my code below. I am not sure why this is not timing out, but just hangs when it doesn't receive a segment. Does this only work on sockets that are not bound using the bind() method?

#define TIMEOUT_MS      100     /* Seconds between retransmits */

if ((rcv_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    DieWithError("socket() failed");

if ((rcv_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    DieWithError("socket() failed");

//set timer for recv_socket
static int timeout = TIMEOUT_MS;
setsockopt(rcv_sock, SOL_SOCKET, SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));

if(recvfrom(rcv_sock, ackBuffer,sizeof(ackBuffer), 0,
       (struct sockaddr *) &servAddr2, &fromSize) < 0){
    //timeout reached
    printf("Timout reached. Resending segment %d\n", seq_num);
    num_timeouts++;
}
2
It looks like you are not checking the return value from setsockopt to see if it returned an error. It probably is. SO_RCVTIMEO is documented on both Linux and MacOS to take a struct timeval, but you are passing an int. Try passing a struct timeval instead. Also, why are you casting &timeout to char *? It's not a char * at all.Celada

2 Answers

63
votes

The SO_RCVTIMEO option expects a struct timeval defined in sys/time.h, not an integer like you're passing to it. The timeval struct has as field for seconds and a field for microseconds. To set the timeout to 100ms, the following should do the trick:

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
if (setsockopt(rcv_sock, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0) {
    perror("Error");
}
4
votes

I have the same problem. I tried to adopt the solution you suggested, using the timeval struct. But it did not seem to work.

I have read on the Microsoft documentation and the time should be a DWORD with the number of milliseconds, but there is also another thing to do, If the socket is created using the WSASocket function, then the dwFlags parameter must have the WSA_FLAG_OVERLAPPED attribute set for the timeout to function properly. Otherwise the timeout never takes effect.