问题描述
这是我的代码,我想专注于对 WSARecv
和 WSAGetoverlappedResult
的调用:
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <mswsock.h> // for ACCEPT_CONTEXT
#include <windows.h>
#include <stdio.h> // for wprintf
#include <assert.h>
static char buf[128];
static WSABUF recvBuf = {.len = 128,.buf = buf };
int WINAPI wWinMain(
HINSTANCE hInstance __attribute__((unused)),HINSTANCE hPrevInstance __attribute__((unused)),LPWSTR pCmdLine __attribute__((unused)),int nCmdshow __attribute__((unused))
) {
WSADATA wsaData = {0};
if (WSAStartup(MAKEWORD(2,2),&wsaData) != 0) {
wprintf(L"WSAStartup Failed,quitting...\n");
abort();
}
SOCKET listenSock = socket(
AF_INET,SOCK_STREAM,IPPROTO_TCP
);
if (listenSock == INVALID_SOCKET) {
wprintf(L"Invalid socket,quitting...\n");
goto cleanupWsa;
}
struct sockaddr_in sockAddr = {
.sin_family = AF_INET,.sin_port = htons(8080),.sin_addr = INADDR_ANY,};
wchar_t pretty[128];
DWORD len = sizeof pretty / sizeof(*pretty);
if (0 != WSAAddresstoString(
(LPSOCKADDR) &sockAddr,sizeof sockAddr,NULL,pretty,&len)
) {
wprintf(L"Error during AddresstoString,"
L"check needed address string length %ld,quitting...\n",len);
goto cleanupSocket;
}
wprintf(L"Binding to %ls\n",pretty);
if (bind(listenSock,(LPSOCKADDR) &sockAddr,sizeof sockAddr)
== SOCKET_ERROR) {
wprintf(L"Bind error,quitting...\n");
goto cleanupSocket;
}
if (listen(listenSock,SOMAXCONN) != 0) {
wprintf(L"Listen error,quitting...\n");
goto cleanupSocket;
}
struct sockaddr s;
int socklen = sizeof s;
SOCKET acceptSocket = accept(listenSock,&s,&socklen);
wprintf(L"ACCEPT\n");
if (SOCKET_ERROR == setsockopt(
acceptSocket,SOL_SOCKET,SO_UPDATE_ACCEPT_CONTEXT,(char *)&listenSock,sizeof listenSock
)) {
wprintf(L"setsockopt update_accept_context failure\n");
goto cleanupBothSockets;
}
DWORD flags = WSA_FLAG_OVERLAPPED;
WSAOVERLAPPED w;
memset(&w,sizeof w);
if (WSARecv(
acceptSocket,&recvBuf,1,//dwBufferCount
NULL,//lpBytesReceived
&flags,&w,NULL
) != SOCKET_ERROR) {
wprintf(L"Recv: expected SOCKET_ERROR\n");
goto cleanupBothSockets;
}
int errnr = WSAGetLastError();
if (errnr != WSA_IO_PENDING) {
wprintf(L"RecvErr %d\n",errnr);
goto cleanupBothSockets;
}
wprintf(L"Running GetoverlappedResult\n");
DWORD bytes_transferred;
BOOL ret = WSAGetoverlappedResult(acceptSocket,&bytes_transferred,TRUE,&flags);
if (ret == FALSE) {
wprintf(L"OverlappedResult: error\n");
} else {
wprintf(L"OverlappedResult: finished with no error,bytes transferred %d",bytes_transferred);
}
cleanupBothSockets:
closesocket(acceptSocket);
cleanupSocket:
closesocket(listenSock);
cleanupWsa:
WSACleanup();
return 0;
}
当我运行程序时,它会侦听连接。我与netcat连接。连接后,它会按预期打印“接受”。它还打印“Running GetoverlappedResult”,但即使我在 netcat 上写了很多胡言乱语,“OverlappedResult:”行都没有出现,直到我在 netcat 上按下 Control-C,并且连接中断。然后,我点击了成功分支,它说传输了 0 个字节。
这个程序有什么问题?为什么 WSAGetoverlappedResult
不会在 128 字节缓冲区填满后立即返回?
我仔细检查了我对 WSARecv
和 WSAGetoverlappedResult
的调用。以下是一些细节:
- 我将
WSARecv
的套接字设置为acceptSocket
,因为我想从已建立的具体连接中读取数据。 - 我将缓冲区计数设置为 1,因为我只有一个缓冲区,而 C 在内存中不区分具有一个元素的数组和只有一个元素的数组。所以我可以将
recvBuf
用作单一大小的数组。 - 我将
lpNumberOfBytesRecvd
设置为 null,因为 MSDN 告诉我如果我提供lpOverlapped
就这样做。
解决方法
您不能将 WSA_FLAG_OVERLAPPED
传递给 WSARecv
。套接字本身已经重叠或不重叠。如果您改为传递 0
,程序会注意到来自 netcat
的字节。
此外,您不能假设 WSARecv
总是会产生错误 ERROR_IO_PENDING
。数据可能立即可用,在这种情况下,它只会返回 0
。因此,您需要成功地处理这两种情况。
如果您提出请求,例如curl
,数据可能马上可用,程序会抱怨它没有得到 ERROR_IO_PENDING
,这是你用 netcat
在套接字中手动输入内容时得到的。