为什么select()总是超时? (视窗)

时间:2012-11-12 22:04:31

标签: c++ c udp winsock select-function

我正在使用select尝试等待来自网络上其他主机的确认,但它总是返回0.我已经看到其他线程有类似的问题,他们的问题始终是他们没有重置fd_set,或者它们没有为select()的第一个参数传递正确的值。这可能不是造成我问题的原因,因为我正在重置fd_set,并且在Windows中忽略了第一个参数,显然,根据msdn。

while(!done)
{
    //send acknowledgment and sequence number
    sendto(_clientSocket, buff, 2, 0, (LPSOCKADDR)&_sockAddr, sizeof(SOCKADDR));

    //wait for acknowledgment
    struct timeval timeout;
    timeout.tv_sec = _rttInfo.timeout/1000;
    timeout.tv_usec = _rttInfo.timeout * 1000;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(_clientSocket, &fds);

    //wait for client to send an acknowledgement
    //wait for the socket to be ready for reading
    int res = select(0, &fds, NULL, NULL, &timeout);
    int err = WSAGetLastError();

    if(res == 0) //if timed out, retry
    {
        timedOutCount++;
        if(timedOutCount >= MAX_TIMEOUTS)
        {
            cout << "Handshaking complete _" << endl;
            return true;
        }
        else
        {
            cout << "Acknowledgement timed out, resending sequence number and acknowledgement" << endl;
            continue;
        }
    }

//下面还有更多if语句,但它永远不会去那里

无论如何,此时它返回0.我看到客户端和服务器通过套接字向对方发送信息,所以我不认为我发送到客户端的错误地址。以下是发送select()等待的数据包的客户端代码:

buff[0] = _seqNum;
res = sendto(_socket, buff, 2, 0, (LPSOCKADDR) &sa_in, sizeof(SOCKADDR));

我不可能是唯一一个遇到过这个问题的人,有谁知道如何解决这个问题?

编辑:有人问_sockAddr在哪里填写,所以我会在这里包含:在那里的某个地方实例化ClienThread类,你可以看到传入的sockaddr_in。

while(true)
{
    try
    {
        // Wait for connection
        cout << "Waiting for incoming connection... ";

        sockaddr_in clientSockAddr;
        //  int clientSockSize = sizeof(clientSockAddr);

        // Listen in on the bound socket:
        //  hClientSocket = accept(hSocket,
        //  reinterpret_cast<sockaddr*>(&clientSockAddr),
        //  &clientSockSize);
        unsigned char seqNum = 0; 

        int addrSize = sizeof(clientSockAddr);
        //wait for a connection
        int res=0;

        //loop until we get a packet that's from someone new
        bool blocking = true;

        while(blocking)
        {
            res = recvfrom(hSocket, buff, strlen(buff), 0, (LPSOCKADDR)&clientSockAddr, &addrSize);         
            bool foundMatch = false;
            //check if we're already handling this client. If so, keep blocking. Otherwise, break out of the loop
            for(list<ClientThread*>::iterator it = clientList.begin(); it != clientList.end(); it++)
            {
                //compare network addresses
                if((*it)->GetSockAddr().sin_addr.S_un.S_addr == clientSockAddr.sin_addr.S_un.S_addr)
                {
                    foundMatch = true; 
                    break;
                }
            }
            if(!foundMatch)
            {
                blocking = false;
            }
        }
        err =  WSAGetLastError();  


        // Check if we can create a new client thread
        if(res != SOCKET_ERROR && res != 0)
        {
            seqNum = (unsigned char) buff[0]; //get the sequence number for handshaking
            cout << "accepted connection from: " << GetHostDescription(clientSockAddr) << endl;
            //start a client thread, to handle requests from this client.
            ClientThread* pThread = new ClientThread(hSocket, clientSockAddr, this, seqNum);
            clientList.push_back(pThread);
            pThread->start(); 
        }
    //  if (hClientSocket==INVALID_SOCKET)
    //      throw "accept function failed.";
    }
    catch(char* ex)
    {
        cerr << "\nError: " << ex << endl;
        bSuccess = false; 
    }

}

Edit2:经过进一步的调试,我发现sendto的调用正在达到他们预期的目标,因为收到的消息是调用recvfrom,而不是select。但是,我需要能够使用非阻塞调用。

3 个答案:

答案 0 :(得分:3)

sendto()可能会失败,但您没有检查是否有任何错误。 sizeof(SOCKADDR)在最后一个参数中使用是错误的。请改用sizeof(_sockAddr)

if (sendto(_clientSocket, buff, 2, 0, (LPSOCKADDR)&_sockAddr, sizeof(_sockAddr)) == SOCKET_ERROR)
{
    cout << "Handshaking failed, error " << WSAGetLastError() << endl;
    return false;
}

确保您的_sockAddr变量有效sockaddr_insockaddr_in6sockaddr_storage a sockaddr。< / p>

另外,假设_rttInfo.timeout以毫秒表示,您的timeout.tv_usec值计算错误。它应该是这样的:

struct timeval timeout;
timeout.tv_sec = _rttInfo.timeout / 1000;
timeout.tv_usec = (_rttInfo.timeout % 1000) * 1000;

答案 1 :(得分:1)

可以预见,选择超时因为你要求超时(基于rttInfo包含的任何值)。如果你想等待更长时间,请调整rttInfo,或者如果你想等到某事发生,那么为超时指定一个空值。

答案 2 :(得分:0)

选择超时,因为您设置了超时。如果您想要阻止操作,则需要超时为空:http://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85).aspx

正如你所说,第一个参数似乎被忽略了。在线:

int res = select(0, &fds, NULL, NULL, NULL);