20

I'm trying to set up a blocking socket to timeout after 16 ms of trying to recvfrom() on a port. Platform is Windows. I've looked at tons of examples online and it seems really simple I just can't seem to get it to work. Any help would be appreciated!

#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; */
Cory Carlson
  • 503
  • 1
  • 4
  • 9

4 Answers4

28

I looked into the select function and as laura said I should do and got it to work real easily! Thanks!

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); 
Cory Carlson
  • 503
  • 1
  • 4
  • 9
  • So what I understand you send it only one packet each time you run your application. Assume you are sending a thousands of packet inside a loop, this code may crash! How will you solve that problem? – Ahmet Jan 01 '16 at 21:26
  • 1
    This is the best answer. The other answer - SO_RCVTIMEO as a DWORD - will timeout the recvfrom, but it has a few problems: 1) if timeout happens socket is no longer in a valid state. 2) I couldn't find any Windows documents that explained how to turn of the timeout...but as your socket will be invalidated, I guess you'd have to make a new one anyway. – Gabe Halsmer Feb 20 '18 at 15:31
20

I tried it by passing it like the folloing

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

and run it!!

Aurélien B
  • 4,590
  • 3
  • 34
  • 48
  • 1
    Hi, welcome to SO. Please try to explain your answer in words, not just in code. It will be easier for everyone to learn that way. Also, since you are answering an old question, how does your answer differ from the other answers? – Olle Sjögren Nov 07 '12 at 09:17
  • From the documentation: `The level at which the option is defined (for example, SOL_SOCKET).` `SO_RCVTIMEO DWORD Sets the timeout, in milliseconds, for blocking receive calls.` – Andrew Apr 14 '18 at 21:34
12

WINDOWS: Timeout value is a DWORD in milliseconds, address passed to setsockopt() is const char *

LINUX: Timeout value is a struct timeval, address passed to setsockopt() is const void *

Source: http://forums.codeguru.com/showthread.php?t=353217

shawn
  • 4,063
  • 7
  • 37
  • 54
1

I'm guessing Windows from the WSASocket() call. If so you're passing the timeout incorrectly.

MSDN says that SO_RCVTIMEO takes an int param that specifies the timeout in ms.

Len Holgate
  • 21,282
  • 4
  • 45
  • 92
  • I've also tried it by passing it like the following: int ms = 16; if(setsockopt(mHandle, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&ms), sizeof(int))) return 1; I still have the same result.. – Cory Carlson Dec 01 '09 at 21:30
  • Thanks, this is just what I needed for a BSD socket system that does not accept the SO_RCVTIMEO – Ignacio Soler Garcia Jan 22 '10 at 08:22
  • It also says "If a blocking receive call times out, the connection is in an indeterminate state and should be closed. 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.", so I'm not sure if it is to be used regularly... – Marki555 Feb 13 '13 at 16:52
  • Looking at this page: http://msdn.microsoft.com/en-gb/library/windows/desktop/ms740476(v=vs.85).aspx I don't see the mention of WSA_FLAG_OVERLAPPED (and it doesn't really make sense to me for an overlapped socket to have a recv timeout set via SO_RCVTIMEO – Len Holgate Feb 13 '13 at 18:31