准确设置Winsock Select超时时间

5
我正在尝试使我的accept调用在指定的时间段后超时,我尝试遵循以下建议:Winsock accept timeout。在这种情况下,当我调用它时,将TIMEVAL结构体传递给select,问题是当我将tv.tv_usec设置为大约40分钟左右时,select调用立即超时,而不是等待我指定的40分钟。MSDN说明select的超时是它等待的最长时间,如何使其在超时之前等待特定的时间段,无论是对于select还是accept呢?
#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include <WinSock2.h>
#include <Ws2tcpip.h>

#include <cstdio>
#include <tchar.h>

VOID _tmain( int argc, TCHAR *argv[] )
{
    WSADATA wsaData = { 0 };
    ADDRINFOA hINTs = { 0 };
    PADDRINFOA pResult = NULL;
    SOCKET hServerSocket = INVALID_SOCKET,
           hClientSocket = INVALID_SOCKET;
    TIMEVAL tv = { 0 };
    INT iReturnStatus = -1;
    DWORD dwRecvTimeout = 30000, // Milliseconds
          dwSendTimeout = 30000; // Milliseconds
    fd_set readFDs = { 0 };

    if ( WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) )
    {
        _tprintf_s( TEXT( "WSAStartup Failed\n" ) );
        return;
    }

    ZeroMemory( &hINTs, sizeof( hINTs ) );
    hINTs.ai_family = AF_INET;
    hINTs.ai_socktype = SOCK_STREAM;
    hINTs.ai_protocol = IPPROTO_TCP;
    hINTs.ai_flags = AI_PASSIVE;
    if ( getaddrinfo( NULL, TEXT( "9001" ), &hINTs, &pResult ) )
    {
        WSACleanup();
        _tprintf_s( TEXT( "getaddrinfo Failed\n" ) );
        return;
    }

    if ( ( hServerSocket = socket( pResult -> ai_family, pResult -> ai_socktype, pResult -> ai_protocol ) ) == INVALID_SOCKET )
    {
        freeaddrinfo( pResult );
        WSACleanup();
        _tprintf_s( TEXT( "socket Failed\n" ) );
        return;
    }

    int iResult = bind( hServerSocket, ( pResult -> ai_addr ), pResult -> ai_addrlen );
    if ( iResult == SOCKET_ERROR )
    {
        freeaddrinfo( pResult );
        closesocket( hServerSocket );
        WSACleanup();
        _tprintf_s( TEXT( "bind Failed\n" ) );
        return;
    }
    freeaddrinfo( pResult );

    if ( listen( hServerSocket, SOMAXCONN ) )
    {
        closesocket( hServerSocket );
        WSACleanup();
        _tprintf_s( TEXT( "listen Failed\n" ) );
        return;
    }

    hClientSocket = INVALID_SOCKET;

    for ( ;; )
    {
        tv.tv_usec = 2400000000; // microseconds
        FD_ZERO( &readFDs );
        FD_SET( hServerSocket, &readFDs );
        _tprintf( "select()\n" );
        iReturnStatus = select( 0, &readFDs, NULL, NULL, &tv );

        // Select Error
        if ( iReturnStatus == SOCKET_ERROR )
        {
            _tprintf( "select Failed\n" );
        }
        // Select Success
        else if ( iReturnStatus )
        {
            // Connection Established On Server Socket
            if ( FD_ISSET( hServerSocket, &readFDs ) )
            {
                // Accept Client Connection
                hClientSocket = accept( hServerSocket, NULL, NULL );
                if ( hClientSocket == INVALID_SOCKET )
                {
                    _tprintf( "accept Failed\n" );
                }
                else
                {
                    // Set Recv Timeout
                    setsockopt( hClientSocket, SOL_SOCKET, SO_RCVTIMEO, ( const char * ) &dwRecvTimeout, sizeof( dwRecvTimeout ) );

                    // Set Send Timeout                 
                    setsockopt( hClientSocket, SOL_SOCKET, SO_SNDTIMEO, ( const char * ) &dwSendTimeout, sizeof( dwSendTimeout ) );

                    // Process Client Request(s)
                    // HandleConnection( ClientSocket );
                }
            }
            // Connection Established On Unknown Socket
            else
            {
                _tprintf( "Invalid Socket Returned\n" );
            }
        }
        // Select Timeout
        else
        {
            _tprintf( "select Timeout\n" );
        }

    }
    if ( hServerSocket != INVALID_SOCKET )
        closesocket( hServerSocket );

    return;
}

1
MSDN表示,select等待结束的最长时间是因为如果套接字在accept上不会阻塞,则会结束。因此,要么您的select调用不正确,要么监听套接字上有可用连接。如果您发布一些代码,可能会有帮助。 - Gene
添加代码,使用的单位应该是正确的。 - shawn
此外,我知道这是一个仅限于Windows的程序,但你应该养成一个好习惯,正确设置select的第一个参数,以防将来在使用该参数的平台上需要执行此操作时忘记它。 - Some programmer dude
3
因为你在设置超时时间大于1秒时使用tv_sec,而在设置小于1秒的超时时间时使用tv_usec,此外你不应允许tv_usec的值大于1,000,000。请注意,本翻译尽量保持原文意思的基础上简洁易懂,不包含额外解释。 - paulsm4
2
tv.tv_sec = ( timeout_in_microseconds / 1000000 ); tv.tv_usec = ( timeout_in_microseconds % 1000000 ); - bob2
显示剩余4条评论
2个回答

4

为什么会这样呢?

这是根据定义来的。

MSDN逐字翻译:

tv_sec 时间间隔,以秒为单位。

tv_usec 时间间隔,以微秒为单位。此值与 tv_sec 成员一起使用,用于表示不是秒的倍数的时间间隔值。


2
问题出在tv.tv_usec上,根据手册,tv_usec是‘long’类型。这个值(2400000000)超出了long的范围,这就是为什么你会得到这样的select系统行为的原因。
如果你想让select等待40分钟,那么请确保使用tv.tv_sec。

long 类型的取值范围是从 -2147483648 到 2147483647。 - Viswesn
我想知道tv_usec=2147483647是否真的会导致超时2147秒,但是0.483647秒... - 现在没有win32可用来检查。 - alk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接