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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Win7机器上没有兑现TCP缓冲区参数相关的知识,希望对你有一定的参考价值。

注意:我已经使用编程和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!
"; 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
", 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!
", strlen("Hello from the client!
"), 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
", 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!
", strlen("Hello from the server!
"), 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. 这些不是“窗口大小”。它们是发送和接收缓冲区大小。
  2. 没有'输出窗口大小'这样的东西。有一个接收窗口和一个拥塞窗口,后者与您的问题无关。
  3. 发送缓冲区大小与接收窗口大小完全无关,接收缓冲区大小仅确定最大接收窗口大小。
  4. 实际的接收窗口大小由协议动态调整。这是您在Wireshark中看到的实际大小。
  5. 该规范有权按平台调整发送和接收缓冲区的提供值,如果您想确定它们的真实含义,文档会建议您获取相应的值。

这里没有问题需要解决。

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

以上是关于Win7机器上没有兑现TCP缓冲区参数的主要内容,如果未能解决你的问题,请参考以下文章

机器人控制tcp通信参数调优

基于tcp协议下粘包现象和解决方案

socket缓冲区以及阻塞模式

如何使用代码删除 tcp / udp 连接?

Linux TCP队列相关参数的总结

Tcp参数优化