0

我正在使用 select 尝试等待来自网络上另一台主机的确认,但它总是返回 0。我见过其他有类似问题的线程,他们的问题总是要么他们没有重置 fd_set,要么他们没有为 select() 的第一个参数传递正确的值。这不可能是导致我的问题的原因,因为我正在重置 fd_set,并且显然,根据 msdn 的说法,第一个参数在 windows 中被忽略了。

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

//下面还有更多 else 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。但是,我需要能够使用非阻塞调用。

4

3 回答 3

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_in6, 或sockaddr_storage, 而不是sockaddr.

在单独的说明中,假设_rttInfo.timeout以毫秒为单位,您的timeout.tv_usec值被计算错误。它应该是这样的:

struct timeval timeout;
timeout.tv_sec = _rttInfo.timeout / 1000;
timeout.tv_usec = (_rttInfo.timeout % 1000) * 1000;
于 2012-11-12T23:00:00.843 回答
1

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

于 2012-11-12T22:45:07.077 回答
0

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

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

int res = select(0, &fds, NULL, NULL, NULL);
于 2012-11-12T22:41:49.877 回答