0

这是我的代码,我想专注于对WSARecvand的调用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, 0, 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, &w, &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。以下是一些细节:

  • 我将套接字设置为WSARecvto acceptSocket,因为我想从已建立的具体连接中读取。
  • 我将缓冲区计数设置为 1,因为我只有一个缓冲区,而 C 在内存中不区分具有一个元素的数组和只有一个元素。所以我可以将recvBuf用作一个大小的数组。
  • 我设置lpNumberOfBytesRecvd为 null,因为 MSDN 告诉我如果我提供lpOverlapped.
4

1 回答 1

0

你不能WSA_FLAG_OVERLAPPED传给WSARecv. 套接字本身已经重叠或不重叠。如果您0改为通过,程序会注意到来自netcat.

此外,您不能假设WSARecv总是会产生错误ERROR_IO_PENDING。数据可能立即可用,在这种情况下,它只会返回0。因此,您需要将这两种情况都视为成功。

如果您使用 eg 发出请求curl,数据可能会立即可用,并且程序会抱怨它没有得到ERROR_IO_PENDING,当您使用 手动将内容输入套接字时会得到netcat

于 2021-05-14T07:01:57.167 回答