Win7机器上没有兑现TCP缓冲区参数

时间:2018-03-01 21:46:14

标签: windows-7 network-programming winsock winsock2

注意 :我已使用编程和Windows网络标记对此进行了标记,所以请不要大声喊叫,我只是在尝试向尽可能多的人展示这一点!

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

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

客户端:

#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;
}

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

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捕获流量时,我看到客户端窗口大小设置正常,但服务器的窗口大小不是我设置的,它是8192:

Window Size

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

有趣的是,相同的代码在Windows 10计算机上运行良好,这让我觉得它是Windows 7特有的。但是,我对我的代码并不是100%肯定,可能会有一些错误。

有人可以建议我如何让Windows遵守我要求的参数吗?

1 个答案:

答案 0 :(得分:1)

  1. 这些不是'窗口大小'。它们是发送和接收缓冲区大小。
  2. 没有'输出窗口大小'这样的东西。有一个接收窗口和一个拥塞窗口,后者与您的问题无关。
  3. 发送缓冲区大小与接收窗口大小完全无关,接收缓冲区大小仅确定最大接收窗口大小。
  4. 实际的接收窗口大小由协议动态调整。这是您在Wireshark中看到的实际大小。
  5. 规范有权按平台调整发送和接收缓冲区的提供值,如果您想确定它们是什么,文档会建议您获取相应的值真的是。
  6. 这里没有问题需要解决。

    注意如果已在侦听套接字上设置接收窗口,则不必在接受的套接字上设置接收窗口大小。它是遗传的。