【问题标题】:Timeout in connect() function from WinsockWinsock 的 connect() 函数超时
【发布时间】:2016-05-24 01:17:07
【问题描述】:

当我从 Winsock 调用 connect() 函数时,有什么方法可以减少超时? 我觉得差不多 30 秒,我想放 5 秒。

【问题讨论】:

  • 不清楚你想做两件截然不同的事情中的哪一件。您想更改connect 调用返回之前的时间吗?或者你想改变实际连接操作愿意等待多长时间?最好的解决方案可能是不管等待多长时间,connect 的等待时间都不会超过 5 秒。
  • 是的,我想更改连接调用返回前的时间。如果在 5 秒内它不起作用,我放弃并继续。
  • 无法更改connect() 本身在退出前等待的时间。这是一种全有或全无的功能。但是您可以将套接字置于异步/非阻塞模式,以便connect() 将立即退出,然后您可以使用select()WSAEventSelect()WSAAsyncSelect() 使用您想要的任何超时来处理等待。

标签: c++ visual-studio sockets winapi winsock


【解决方案1】:

最简单的方法是在连接的时候以非阻塞模式使用socket,并使用select(),超时5秒来检查socket是否可写。 select() 退出后,连接要么建立,要么不建立。如果不是,请考虑连接超时并根据需要执行错误处理。

【讨论】:

  • 但是这样的话,是必须要建立连接的吧?
  • @cna,“必须”是什么意思?最多 5 秒后,您将重新获得控制权,并且您将知道它是否已建立。
  • @cna:当您的代码继续执行其他操作时,非阻塞套接字会在后台建立连接。因此,将套接字设置为非阻塞模式(ioctlsocket(FIONBIO) 设置为 1),然后调用connect()。如果connect() 返回SOCKET_ERROR 并且WSAGetLastError() 返回WSAEWOULDBLOCK,则连接正在进行中,所以调用select() 并有5 秒的超时。如果select() 返回成功,表明套接字是可写的,则连接已经建立。否则,关闭套接字。
  • 谢谢,我必须创建任何特定类型的套接字吗?会不会是SOCKET socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
  • @RemyLebeau,谢谢。但是我尝试的所有时间,select() 都会返回超时,即使套接字的另一端可用。我正在这样做:timeval timeOut = {0}; timeOut.tv_sec = 10; timeOut.tv_usec = 0; fd_set fdset = {0}; fdset.fd_count = 1; fdset.fd_array[0] = socket; iRet = select(1, &fdset, NULL, NULL, &timeOut);
【解决方案2】:

您还可以调用 ConnectEx() 函数并将带有预先创建的事件的 OVERLAPPED.hEvent 结构传递给它,您可以根据需要使用 WaitForSingleObject() 等待。

ConnectEx() 仅适用于 WindowsXP 及更高版本...

//The HANDLE Socket MUST BE pre-bound with Bind() before calling this function
int ConnectWithTimout(HANDLE Socket, UINT remIP, WORD remPort, UINT milliseconds)
{
    int iRes, Result;
    UINT OptVal, Flags;
    OVERLAPPED Overlapped;
    sockaddr_in socket_info;


    Result= ERROR_UNEXP_NET_ERR;

    ZeroMemory(&socket_info, sizeof(socket_info));
    ZeroMemory(&Overlapped, sizeof(Overlapped));

    socket_info.sin_addr.S_addr = htonl(remIP);
    socket_info.sin_port = htons(remPort);
    socket_info.sin_family = AF_INET;

    Overlapped.hEvent = WSACreateEvent(); 
    if ( ConnectEx(Socket, &socket_info, sizeof(socket_info), NULL, 0, NULL, &Overlapped) )
        printf("WOW! Connection succeeded immediately\n");
    else
    {
        iRes = WSAGetLastError();
        if (iRes == ERROR_IO_PENDING)
        {
            iRes = WaitForSingleObject(Overlapped.hEvent, milliseconds);  //Wait for x milliseconds to connect
            if (iRes == WAIT_OBJECT_0)
            {
                if (!WSAGetOverlappedResult(socket, &Overlapped, &OptVal, FALSE, Flags))
                {
                    iRes = WSAGetLastError();
                    if (iRes == WSAEADDRINUSE)
                        DoError("WSAGetOverlappedResult() reported that the requested local address is already in use or in a TIME_WAIT state")
                    else
                        DoError("WSAGetOverlappedResult() failed with error: ", iRes);
                }
                else
                {
                    OptVal = 1;
                    iRes = setsockopt(Socket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, PCHAR(&OptVal), sizeof(OptVal));
                    if (iRes == SOCKET_ERROR)
                        DoError("setsockopt(SO_UPDATE_CONNECT_CONTEXT) failed with error: ", WSAGetLastError() );

                    printf("Connected to %s : %s\n", inet_ntoa(socket_info.sin_addr), itoa(ntohs(socket_info.sin_port)));
                    Result=NO_ERROR;
                }
            }
            else
            {
                if (iRes == WAIT_TIMEOUT)
                {
                    DoWarning("ConnectEx() TIMED OUT - ", iRes);
                    Result= ERROR_TIMEOUT;
                }
                else
                    DoError("ConnectEx() failed with error: ", iRes)
            }
        }
        else if (iRes ==  WSAECONNREFUSED)  //After this error, it is OK to try to connect again on this docket.
            DoWarning("ConnectEx() failed with CONNECTION REFUSED: ", 0 )
        else if (iRes =  WSAENETUNREACH)    //After this error, it is OK to try to connect again on this docket.
            DoWarning("ConnectEx() failed with NETWORK UNREACHABLE: ", 0 )
        else if (iRes =  WSAETIMEDOUT)  //After this error, it is OK to try to connect again on this docket.
        {
            DoWarning("ConnectEx() TIMED OUT Immediately:", 0 );
            Result= ERROR_TIMEOUT;
        }
        else
            DoError("ConnectEx() failed with unexpected error: ", iRes )
    }


    WSACloseEvent(Overlapped.hEvent);

    return Result;
}

【讨论】:

  • 这似乎是一个不错的解决方案,但它需要更多的工作,而且 ConnectEx 会返回 10049s。我最终使用了WSAConnectByName
  • 更多完整示例请参见此处:gist.github.com/joeyadams/4158972
猜你喜欢
  • 2023-03-17
  • 2012-04-21
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
相关资源
最近更新 更多