19

我正在用 C++ 开发一个 RTSP 源过滤器,并且我正在使用 WINSOCK 2.0 - 阻塞套接字。

当我创建一个阻塞套接字时,我将其设置SO_RCVTIMEO为 3 秒,如下所示:

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

我的过滤器尝试连接IP_ADDRESS:554(554 是 RTSP 服务器端口)。如果有服务器在端口 554 上侦听该 IP,则一切正常,但是:

  1. 如果我的过滤器创建一个到现有 IP 地址的套接字,但在没有人监听的随机端口上,connect()等待 3 秒并返回WSAETIMEDOUT。所以 3 秒后,我知道提供的 URL 是错误的。

  2. 如果我的过滤器为不存在的 IP 地址创建一个套接字并尝试连接它,它会在返回 SOCKET_ERROR 之前挂起大约 10 秒。因此,SO_RCVTIMEO如果网络上不存在 IP,则会被忽略...

问题: 在第二种情况下,如何为不存在的 IP 设置超时?我是否需要先发送 ICMP PING 以查看 IP 是否存在,或者执行其他类似的检查?

任何帮助将不胜感激。谢谢。:)

我的问题的答案

因为我正在使用阻塞套接字,所以调用connect()阻塞,直到建立连接,或者连接失败,因为主机没有响应,或者它拒绝连接。如果我将套接字的超时设置为 3 秒,并尝试连接到不存在的主机,我的电脑(客户端)将发送SYN设置了标志的 TCP 数据包,以启动三向握手。通常,如果主机启动,将响应包含 TCP 数据包ACKSYN设置标志,然后客户端(我)将发送带有ACK标志设置的 TCP 数据包。然后建立连接。但是,如果主机关闭并SYN发送,客户端会等待 3 秒超时到期,然后再尝试一次,再一次,直到TcpMaxConnectRetransmissions微软文章)注册表设置已达到,因为主机可以启动但SYN数据包可能会丢失......我的Windows XP有这个设置为4,我猜,所以每次它尝试发送SYN时,它等待3秒,当第四尝试失败,它返回SOCKET_ERROR(12 秒后),并设置WSAETIMEDOUT为最后一个 WSA 错误。

解决这个问题的方法是使用非阻塞套接字,并connect()按照 Martin James 的建议尝试手动测量连接尝试时间(因为现在不会阻塞)。

另一种方法是摆弄注册表,这是最后的手段......

4

3 回答 3

2

硬着头皮。远程 IP 可能没有运行 PING 服务器,或者 PING 可能被某些路由器阻止,因此没有任何帮助。你能不能只等待 10 秒然后做出你使用的任何错误指示?

如果您绝对必须在 3 秒后超时尝试连接,您可以自己将其超时。

于 2011-06-01T09:48:32.233 回答
1

实际上,Berkeley 套接字连接没有超时,所以你不能设置它。ICMP PING 没有帮助,我不知道为什么,但是如果主机不存在,您将花费大约 1 秒的时间来使用 PING。尝试使用 ARP 检测主机是否存在。

于 2011-06-01T10:35:51.550 回答
-1

从 cmd 你可以用像这样的超时 ping ip 'ping -w 100 -n 1 192.168.1.1'

它将在 100 毫秒内返回

你可以通过'echo %errorlevel% 0 = ok, 1 = fail来检查返回码,然后你知道你是否应该尝试连接

在 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;
} 
于 2013-04-04T08:37:51.217 回答