C++ WINSOCK - 为不存在的 IP 上的连接尝试设置超时?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6199122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:40:09  来源:igfitidea点击:

WINSOCK - Setting a timeout for a connection attempt on a non existing IP?

c++winsock2

提问by Cipi

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

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

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

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

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:

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

  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_RCVTIMEOgets ignored if the IP doesn't exist on the network...

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

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

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?

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

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 SYNflag set, to initiate the Threeway handshake. Normally, the host, if up, will respond with TCP packet containing ACKand SYNflags set, and then, client (me) would send the TCP packet with ACKflag set. Then the connection is made. BUT if the host is down, and the SYNis 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 SYNpacket 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 WSAETIMEDOUTas the last WSA error.

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

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.

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

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

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

采纳答案by Martin James

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?

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

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

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

回答by xandox

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.

实际上,Berkeley 套接字没有连接超时,因此您无法设置它。ICMP PING 没有帮助,我不知道为什么,但是如果主机不存在,您会在 PING 上花费大约 1 秒钟。尝试使用 ARP 检测主机是否存在。

回答by steveh

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

从 cmd 中,您可以 ping ip 超时,例如“ping -w 100 -n 1 192.168.1.1”

it will return within 100mS

它会在 100 毫秒内返回

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

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

in c++

在 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;
}