我正在使用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。但是,我需要能够使用非阻塞调用。
答案 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_in
,sockaddr_in6
或sockaddr_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);