我的软件的一些用户最近来找我,告诉我它在Windows 8上不起作用。经过调查后发现,由于一些奇怪的原因,我的服务器套接字并不总是接受连接,但让我们他们超时了。
更奇怪的是:连接到localhost时也会发生这种情况,而不仅仅是在远程访问它时。
“你有什么尝试?”
提醒:完全相同的代码在Windows XP和Windows 7上运行正常,它也会影响localhost连接(不是硬件问题)。此外,只有三分之一的连接失败,其余的工作正常。
好的,现在是一些真正的代码,因为它比所有这些单词更有用。
套接字设置:
int iResult;
struct addrinfo *result = NULL;
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// "Resolve" our localhost
iResult = getaddrinfo(NULL, port, &hints, &result);
if (iResult != 0) {
printf("error (2) : %d\n", iResult);
return false;
}
// Create the socket
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (listenSocket == INVALID_SOCKET) {
freeaddrinfo(result);
printf("error (3) : %d\n", WSAGetLastError());
return false;
}
// Bind it
iResult = bind(listenSocket, result->ai_addr, result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
freeaddrinfo(result);
closesocket(listenSocket);
printf("error (4) : %d\n", WSAGetLastError());
return false;
}
freeaddrinfo(result);
// Listen
iResult = listen(listenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
closesocket(listenSocket);
printf("%d\n", WSAGetLastError());
return false;
}
正如您可能看到的,它几乎直接来自MSDN,应该没问题。此外,它适用于2/3的连接,所以我真的怀疑它是错误的设置代码。
收件人代码:
if (listenSocket == INVALID_SOCKET) return false;
#pragma warning(disable:4127)
fd_set fds;
SOCKET client;
do {
FD_ZERO(&fds);
FD_SET(listenSocket, &fds);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
if (!select(1, &fds, NULL, NULL, &timeout)) continue; // See you next loop!
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
// Accept the socket
client = accept(listenSocket, (struct sockaddr *)&addr, &addrlen);
if (client == INVALID_SOCKET) {
printf("[HTTP] Invalid socket\n");
closesocket(listenSocket);
return false;
}
// Set a 1s timeout on recv()
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
// Receive the request
char recvbuf[513];
int iResult;
std::stringbuf buf;
clock_t end = clock() + CLOCKS_PER_SEC; // 1s from now
do {
iResult = recv(client, recvbuf, 512, 0);
if (iResult > 0) {
buf.sputn(recvbuf, iResult);
} else if (iResult == 0) {
// Hmm...
} else {
printf("[HTTP] Socket error: %d\n", WSAGetLastError());
break;
}
} while (!requestComplete(&buf) && clock() < end);
此代码吐出“[HTTP]套接字错误:10060”错误,因此之后的任何代码都相当无关。
select
调用是存在的,因为实际的循环也做了其他一些事情,但是我把它留了出去,因为它不是与套接字相关的。
更奇怪的是:根据Wireshark的说法,Windows似乎在制造实际的网络错误:http://i.imgur.com/BIrbD.png
我一直试图解决这个问题一段时间了,我可能只是在做一些愚蠢的事情,所以我非常感谢你的所有答案。
答案 0 :(得分:4)
我一直在研究这个烦人的问题一整天,并设法通过从头开始重写整个服务器并以不同方式实现它来最终解决它。我确实将问题追溯到setsockopt
,这似乎不再使SO_RCVTIMEO
非常好,导致超时达到零秒,这使得随机连接超时。
我的新实现不再使用超时,现在只是非阻塞和异步。效果很好,但需要更多代码。
我将假设它只是Windows 8中的一个错误,它将在发布之前通过更新修复。我怀疑微软想要像这样改变伯克利套接字API。
答案 1 :(得分:3)
在Windows中,SO_RCVTIMEO选项要求MILLISECONDS中的DWORD参数,但不是时间结构。 请参阅http://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx。
传递timeval会导致窗口将其解释为DWORD,并且读取秒成员,因为它是毫秒。 我不知道为什么timeval参数在Win之前的8中起作用,可能它是未记录的功能,在win 8中被删除。