Win7 机器上不支持 TCP 缓冲区参数

TCP buffer parameters not being honoured on Win7 machine

注意:我已经用编程和windows网络标签标记了这个,所以请不要喊叫,我只是在尝试向尽可能多的人公开这一点!

我正在尝试为我编写的小型客户端和服务器设置接收和发送缓冲区,这样当我执行网络捕获时,我会看到我在 TCP 握手中设置的 window 大小.

对于程序员,请考虑以下非常简单的客户端和服务器代码。
对于none-程序员,请跳过这部分到我的图片。

客户:

#include <WinSock2.h>
#include <mstcpip.h>
#include <Ws2tcpip.h>
#include <thread>
#include <iostream>

using namespace std;


int OutputWindowSize(SOCKET s, unsigned int nType)
{
    int buflen = 0;
    int nSize = sizeof(buflen);
    if (getsockopt(s, SOL_SOCKET, nType, (char *)&buflen, &nSize) == 0)
        return buflen;
    return -1;
}

bool SetWindowSizeVal(SOCKET s, unsigned int nSize)
{

    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&nSize, sizeof(nSize)) == 0)
        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&nSize, sizeof(nSize)) == 0)
            return true;
    return false;
}

int main(int argc, char** argv)
{
    if (argc != 3) { cout << "not enough args!\n"; return 0; }
    const char* pszHost = argv[1];
    const int nPort = atoi(argv[2]);

    WSADATA wsaData;
    DWORD Ret = 0;
    if ((Ret = WSAStartup((2, 2), &wsaData)) != 0)
    {
        printf("WSAStartup() failed with error %d\n", Ret);
        return 1;
    }

    struct sockaddr_in sockaddr_IPv4;
    memset(&sockaddr_IPv4, 0, sizeof(struct sockaddr_in));
    sockaddr_IPv4.sin_family = AF_INET;
    sockaddr_IPv4.sin_port = htons(nPort);
    if (!InetPtonA(AF_INET, pszHost, &sockaddr_IPv4.sin_addr)) { return 0; }

    SOCKET clientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  // Create active socket: one which is passed to connect().
    if (!SetWindowSizeVal(clientSock, 12345))
    {
        cout << "Failed to set window size " << endl;
        return -1;
    }
    cout << "Set window size on client socket as: RECV" << OutputWindowSize(clientSock, SO_RCVBUF) <<
        " SEND: " << OutputWindowSize(clientSock, SO_SNDBUF) << endl;

    int nRet = connect(clientSock, (sockaddr*)&sockaddr_IPv4, sizeof(sockaddr_in));
    if (nRet != 0) { return 0; }

    char buf[100] = { 0 };
    nRet = recv(clientSock, buf, 100, 0);
    cout << "Received " << buf << " from the server!" << endl;

    nRet = send(clientSock, "Hello from the client!\n", strlen("Hello from the client!\n"), 0);
    closesocket(clientSock);

    return 0;
}

服务器:

#include <WinSock2.h>
#include <mstcpip.h>
#include <Ws2tcpip.h>
#include <iostream>

using namespace std;

int OutputWindowSize(SOCKET s, unsigned int nType)
{
    int buflen = 0;
    int nSize = sizeof(buflen);
    if (getsockopt(s, SOL_SOCKET, nType, (char *)&buflen, &nSize) == 0)
        return buflen;
    return -1;
}

bool SetWindowSizeVal(SOCKET s, unsigned int nSize)
{

    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&nSize, sizeof(nSize)) == 0)
        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&nSize, sizeof(nSize)) == 0)
            return true;
    return false;
}

int main()
{
    WSADATA wsaData;
    DWORD Ret = 0;
    if ((Ret = WSAStartup((2, 2), &wsaData)) != 0)
    {
        printf("WSAStartup() failed with error %d\n", Ret);
        return 1;
    }

    struct sockaddr_in sockaddr_IPv4;
    memset(&sockaddr_IPv4, 0, sizeof(struct sockaddr_in));
    sockaddr_IPv4.sin_family = AF_INET;
    sockaddr_IPv4.sin_port = htons(19982);
    int y = InetPton(AF_INET, L"127.0.0.1", &sockaddr_IPv4.sin_addr);
    if (y != 1) return 0;
    socklen_t addrlen = sizeof(sockaddr_IPv4);

    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (!SetWindowSizeVal(sock, 12345))
    {
        cout << "Failed to set window size " << endl;
        return -1;
    }
    cout << "Set window size on listen socket as: RECV" << OutputWindowSize(sock, SO_RCVBUF) <<
        " SEND: " << OutputWindowSize(sock, SO_SNDBUF) << endl;

    if (bind(sock, (sockaddr*)&sockaddr_IPv4, sizeof(sockaddr_IPv4)) != 0) { /* error */ }
    if (listen(sock, SOMAXCONN) != 0) { return 0; }

    while (1)
    {
        SOCKET sockAccept = accept(sock, (struct sockaddr *) &sockaddr_IPv4, &addrlen);
        if (!SetWindowSizeVal(sockAccept, 12345))
        {
            cout << "Failed to set window size " << endl;
            return -1;
        }

        cout << "Set window size as on accepted socket as: RECV" << OutputWindowSize(sock, SO_RCVBUF) <<
            " SEND: " << OutputWindowSize(sock, SO_SNDBUF) << endl;

        if (sockAccept == -1) return 0;

        int nRet = send(sockAccept, "Hello from the server!\n", strlen("Hello from the server!\n"), 0);
        if (!nRet) return 0;

        char buf[100] = { 0 };
        nRet = recv(sockAccept, buf, 100, 0);
        cout << "Received " << buf << " from the client!" << endl;

        if (nRet == 0) { cout << "client disonnected!" << endl; }
        closesocket(sockAccept);
    }
    return 0;
}

我程序的输出表明 window 大小已成功设置:

Set window size on listen socket as: RECV12345 SEND: 12345
Set window size as on accepted socket as: RECV12345 SEND: 12345

对于服务器,对于客户端:

Set window size on listen socket as: RECV12345 SEND: 12345

然而,当我使用 RawCap 捕获流量时,我看到客户端 window 大小设置正常,但服务器的 window 大小不是我设置的,它是 8192 :

现在,我已经阅读 this MS link 并且它说要添加注册表值;我这样做了,添加了值 0x00001234,但仍然没有任何区别。

有趣的是,相同的代码在 Windows 10 机器上运行良好,这让我认为它是 Windows 7 特定的。但是,我不是 100% 确定我的代码,其中可能存在一些错误。

谁能建议我如何让 Windows 满足我请求的参数?

  1. 这些不是 'window sizes'。它们是发送和接收缓冲区大小。
  2. 没有'output window size'这样的东西。有接收window和拥塞window,后者与你的问题无关
  3. 发送缓冲区大小与接收window大小完全无关,接收缓冲区大小仅决定最大接收window大小.
  4. 实际接收window大小由协议动态调整。这是您在 Wireshark 中看到的实际大小。
  5. 规范有权向上或向下调整发送和接收缓冲区提供的值,文档建议您获取相应的值(如果需要)确定它们到底是什么。

这里没有问题可以解决

注意 如果您已经在侦听套接字上设置了接受套接字,则不必在接受套接字上设置接收大小 window。是遗传的。