18

I am developing a RTSP Source filter in C++, and I am using WINSOCK 2.0 - blocking socket.

When I create a blocking socket, I set its SO_RCVTIMEO to 3 secs like so:

int ReceiveTimeout = 3000; 
int e = setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&ReceiveTimeout, sizeof(int));

My filter tries to connect to IP_ADDRESS:554 (554 is RTSP server port). If there is a server listening on that IP on the port 554, all goes well, but:

  1. If my filter creates a socket to an existing IP address, but on a random port which no one listens on, connect() waits for 3 secs and returns WSAETIMEDOUT. So after 3 secs, I know that the provided URL is bad.

  2. If my filter creates a socket to a non existing IP address, and tries to connect it, it hangs for about 10 secs before returning SOCKET_ERROR. So, SO_RCVTIMEO gets ignored if the IP doesn't exist on the network...

QUESTION: How can I set the timeout for a non existing IP, in the second case? Do I need to send ICMP PING first to see does the IP exist, or perform some other check like that?

Any help will be appreciated. Thanx. :)

THE ANSWER TO MY PROBLEM

Because I am using blocking sockets, call to connect() blocks, until the connection is made, or the connection fails because the host is not responding, or it is refusing connection. If I set socket's timeout to be 3 seconds, and try to connect to a host that doesn't exist, my pc (client) will send TCP packet with SYN flag set, to initiate the Threeway handshake. Normally, the host, if up, will respond with TCP packet containing ACK and SYN flags set, and then, client (me) would send the TCP packet with ACK flag set. Then the connection is made. BUT if the host is down, and the SYN is sent, client waits until the 3 second timeout expires, and then tries AGAIN, and AGAIN, until the TcpMaxConnectRetransmissions (MICROSOFT ARTICLE) registry setting is reached, because the host can be UP but the SYN packet might get lost... My Windows XP has this setting at 4, I guess, so each time it tries to send SYN, it waits 3 seconds, and when the fourth try fails, it returns SOCKET_ERROR (after 12 secs), and sets WSAETIMEDOUT as the last WSA error.

The way around this is using non blocking sockets, and trying to manually measure the connection attempt time (because now the connect() wouldn't block) as Martin James suggested.

Another way is to fiddle with the registry, which is the last resort...

Community
  • 1
  • 1
Cipi
  • 11,055
  • 9
  • 47
  • 60
  • Is this in a windows or a console app? The question is important because it lets me know what tools in the wsapi you have available to you. – johnathan Jun 01 '11 at 12:23
  • It is a DirectShow Push Source filter, DLL library. – Cipi Jun 01 '11 at 12:29
  • I forget , DLLMain pass in an hwnd? If not, the *best* option is to create a hwnd only window (so the only thing you get with it is a message pump and WndProc, set the socket to non blocking , WSAAsync, create a timer off of the socket (it's an unsigned int, exaclty what create timer takes) and when your timer message hits your message loop, you know it's timed out. (it's what i did when i made a socks 5 checker that took a list of socks5 proxies and checked for validity. – johnathan Jun 01 '11 at 12:33
  • I don't have WndProc, but the non-blocking socket idea is good. ;) – Cipi Jun 01 '11 at 12:35
  • well, to get a wind proc (trust me you want it) you'll have to create a window that is just an HWND , ie it has no visual user interface, it provides you a message loop though, with which you can tell the socket to be WSAAsyncSelect() ;) – johnathan Jun 01 '11 at 12:37
  • Maybe [select](http://msdn.microsoft.com/en-us/library/ms740141.aspx) can be useful here? – Bojan Hrnkas Aug 30 '14 at 09:07

3 Answers3

2

Bite the bullet. The remote IP may not be running a PING server or PING may be blocked by some router, so it's no help. Can you not just wait the 10 sec and then make whatever error indication you use?

If you absolutely have to time out the attempted connection after 3 seconds, you can time it out yourself.

0x400921FB54442D18
  • 725
  • 1
  • 5
  • 18
Martin James
  • 24,453
  • 3
  • 36
  • 60
  • 3
    I get the ping catch, but how could I time it out myself? How can I stop the socket's connection attempt from another thread, while it is still trying to connect? I need `connect()` function to return so I can have a working disconnectable socket. Or am I wrong? – Cipi Jun 01 '11 at 10:18
1

Actually, Berkeley sockets have not timeout for connect, so you can not set it. ICMP PING is not helpful, i don't know why, but if host not exists you spend around 1 second with PING. Try use ARP for detect is host exists.

xandox
  • 336
  • 1
  • 2
  • 9
  • 1
    Actually, ARP is not an option, since the servers are on WAN, not on LAN, so ARP wouldn't work... or I am wrong? – Cipi Jun 01 '11 at 10:48
  • [Wiki](http://en.wikipedia.org/wiki/Address_Resolution_Protocol) said that ARP exists at IEEE 802.11 – xandox Jun 01 '11 at 11:26
  • 1
    Well, as I said... on LAN I can UDP broadcast ARP request `WHO HAS 192.168.0.22`, and the interface with `192.168.0.22` responds `I DO, THIS IS MY MAC 00:11:22:33:44:55`. But it sounded impossible that all the PC-s in the world would receive my ARP request on WAN, and then that only one of them would respond. ARP requests never reach out of the local network. So, I tried it, and it works only on LAN... as expected. :) – Cipi Jun 01 '11 at 12:00
  • So it only one way like say **Martin James** – xandox Jun 01 '11 at 12:17
-1

from cmd you can ping the ip with a timeout like this 'ping -w 100 -n 1 192.168.1.1'

it will return within 100mS

you can check the return code by 'echo %errorlevel% 0 = ok, 1 = fail, then you know if you should try connect

in c++

bool pingip_nowait(const char* ipaddr)
{
    DWORD exitCode;

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    si.hStdOutput =  GetStdHandle(STD_OUTPUT_HANDLE);
    si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    si.wShowWindow = SW_HIDE;

    CString cmd = "ping -w 100 -n 1 ";
    cmd += ipaddr;
    if (!CreateProcess(NULL,
        cmd.GetBuffer(),
        NULL,
        NULL,
        FALSE,
        0,
        NULL,
        NULL,
        &si,
        &pi)) {
            TRACE("ERROR: Cannot launch child process\n");
            return false;
    }

    // Give the process time to execute and finish
    WaitForSingleObject(pi.hProcess, 200L);

    if (GetExitCodeProcess(pi.hProcess, &exitCode))
    {
        TRACE("ping returned %d\n", exitCode);
        // Close process and thread handles. 
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
        return exitCode==0 ? true : false;
    }
    TRACE("GetExitCodeProcess() failed\n");
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
    return false;
} 
steveh
  • 1,352
  • 2
  • 27
  • 41