Wad
Wad

Reputation: 1534

TCP buffer parameters not being honoured on Win7 machine

Note: I have tagged this with both programming and windows networking tags, so please don't shout, I'm just trying to expose this to as many people as may be able to help!

I am trying to set the receive and send buffers for a small client and server I have written, so that when I perform a network capture, I see the window size I have set in the TCP handshake.

For the programmers, please consider the following very simple code for a client and server.
For the none-programmers, please skip past this section to my image.

Client:

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

Server:

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

The output from my program states that the window sizes have been set succesfully:

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

for the server, and for the client:

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

However, when I capture the traffic using RawCap, I see that the client window size is set fine, but server's window size is not what I set it to be, it is 8192:

Window Size

Now, I have read this MS link and it says to add a registry value; I did this, adding the value 0x00001234, but it still made no difference.

The interesting thing is, the same code works fine on a Windows 10 machine, which makes me think it is Windows 7 specific. However, I'm not 100% sure on my code, there might be some errors in it.

Can anyone suggest how I can get Windows to honour my requested parameters please?

Upvotes: 0

Views: 287

Answers (1)

user207421
user207421

Reputation: 311016

  1. These are not 'window sizes'. They are send and receive buffer sizes.
  2. There is no such thing as 'output window size'. There is a receive window and a congestion window, and the latter is not relevant to your question.
  3. The send buffer size has exactly nothing to do with the receive window size, and the receive buffer size only determines the maximum receive window size.
  4. The actual receive window size is adjusted dynamically by the protocol. It is the actual size that you are seeing in Wireshark.
  5. The platform is entitled by the specification to adjust the supplied values for the send and receive buffers up or down, and the documentation advises you to get the corresponding values if you want to be sure what they really are.

There is no problem here to solve.

NB You don't have to set the receive window size on an accepted socket if you already set it on the listening socket. It is inherited.

Upvotes: 1

Related Questions