首页 > 解决方案 > WinSock2 的带有 MSG_WAITALL 标志的 recv 返回不完整的数据包,没有错误

问题描述

WinSock2.h用来从 TCP 套接字接收数据。这是创建套接字并从中接收的代码:

int Socket_Init(SOCKET * pSocket) {
    struct addrinfo lHints;
    struct addrinfo *lResult = nullptr;
    memset(&lHints, 0, sizeof(lHints));

    lHints.ai_family = AF_INET;
    lHints.ai_socktype = SOCK_STREAM;
    lHints.ai_protocol = IPPROTO_TCP;

    if (getaddrinfo(ADDR, PORT_ID, &lHints, &lResult) != 0) {
        return -1;
    }

    *pSocket = socket(lResult->ai_family, lResult->ai_socktype, lResult->ai_protocol);
    if (*pSocket == INVALID_SOCKET) {
        return -2;
    }

    // Set receive timeout.
    uint32_t timeout = 10000;
    if (setsockopt(*pSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
        return -3;
    }
    
    // Set send timeout.
    if (setsockopt(*pSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
        return -4;
    }

    if (connect(*pSocket, lResult->ai_addr, (int)lResult->ai_addrlen) == SOCKET_ERROR) {
        return -5;
    }
    
    return 0;
}

int Socket_Receive(char * buf, int len, SOCKET * pSocket) {
    if (*pSocket == INVALID_SOCKET) {
        return -1;
    }

    int nBytesReceived = recv(*pSocket, buf, len, MSG_WAITALL);
    if (nBytesReceived == 0) {
        return -2;
    } else if (nBytesReceived == SOCKET_ERROR) {
        if (WSAECONNABORTED == LAST_ERROR || WSAECONNRESET == LAST_ERROR) {
            return -3;
        } else {
            return -4;
        }
    }

    return nBytesReceived;
}

我使用此代码接收 131130 字节的数据包。使用 Wireshark,我可以看到在最后一个或倒数第二个分段数据包上重新传输时会发生不完整的数据包(堆栈将消息拆分为 1460 字节 + 标头的块)。发生这种情况时,recv()返回 (131130 - size retransmitted) 而不是 131130。如果我再次读取丢失的字节数,我可以毫无问题地得到它们。

我的困惑来自于我正在使用该MSG_WAITALL标志,这似乎并没有完全发挥作用。从文档中,recv()只有在以下情况下MSG_WAITALL才会返回标志:

但我没有收到错误,连接仍然打开,缓冲区未满。

我看到了这个线程,回复者说MSG_WAITALLTCP 套接字不支持并使用 0 而不是MSG_WAITALL. 我试过了,但这导致我的代码重试次数更多。此外,如果它不受支持,那么我相信该recv()功能应该立即失败WSAEOPNOTSUPP,但事实并非如此。

我错过了什么吗?

标签: cwindowssocketstcpwinsock2

解决方案


推荐阅读