C ++低吞吐量Winsock TCP测试应用程序

时间:2018-08-24 08:25:10

标签: c++ windows tcp winsock asio

我正在尝试构建一个在本地主机上运行的快速服务器和客户端。这个想法是从另一个程序发送数据Blob,并且要很快。一次只有一个客户端连接到服务器。

我首先尝试使用boost :: asio库实现这一点。一切正常,只是吞吐量非常慢,只有415MB / s。

然后,我继续用winsock创建一个测试用例,它的吞吐量也非常相似,为434MB / s。太慢了。

我期望每秒更多40GB或至少几GB的容量。

Id会通过网络编程来解决所有建议,因为我对此一无所知。

我当前的客户端功能:

bool sendDataWin(size_t size, const size_t blocksize, size_t port)
{
    int result;

    struct addrinfo *addressinfo = nullptr, hints;
    auto sport = std::to_string(port);

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the local address and port to be used by the server
    result = getaddrinfo("localhost", sport.c_str(), &hints, &addressinfo);
    if (result != 0) {
        printf("Error at client socket(): %ld\n", WSAGetLastError());
        return false;
    }

    SOCKET connection = socket(
        addressinfo->ai_family,
        addressinfo->ai_socktype,
        addressinfo->ai_protocol);

    if (connection == INVALID_SOCKET) {
        printf("Error at client socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(addressinfo);
        return false;
    }

    // Try to put loopback fast path on.
    bool value;
    DWORD dwBytesRet;
    int status =
        WSAIoctl(
            connection,
            SIO_LOOPBACK_FAST_PATH,
            &value,
            sizeof(bool),
            NULL,
            0,
            &dwBytesRet,
            0,
            0);

    if (status == SOCKET_ERROR) {
        DWORD LastError = ::GetLastError();
        if (LastError == WSAEOPNOTSUPP) {
            printf("client call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
        }
    }

    // Connect to server.
    result = connect(connection, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
    if (result == SOCKET_ERROR) {
        printf("Error at client socket(): %ld\n", WSAGetLastError());
        closesocket(connection);
        return false;
    }
    freeaddrinfo(addressinfo);

    std::vector<uint8_t> buffer;
    buffer.resize(blocksize);

    size_t total = 0;

    do {
        size_t sendSize = blocksize;
        size_t next = total + sendSize;
        if (next > size)
        {
            sendSize -= next - size;
        }

        // Echo the buffer back to the sender
        result = send(connection, (const char*)buffer.data(), (int)sendSize, 0);
        if (result == SOCKET_ERROR) {
            printf("client send failed: %d\n", WSAGetLastError());
            closesocket(connection);
            return false;
        }

        total += sendSize;
    } while (total < size);

    result = shutdown(connection, SD_SEND);
    if (result == SOCKET_ERROR) {
        printf("client shutdown failed: %d\n", WSAGetLastError());
        closesocket(connection);
        return false;
    }
    // cleanup
    closesocket(connection);

    return true;
}

服务器功能:

bool serverReceiveDataWin(size_t size, const size_t blocksize, size_t port)
{
    int result;

    struct addrinfo *addressinfo = nullptr, *ptr = nullptr, hints;
    auto sport = std::to_string(port);

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the local address and port to be used by the server
    result = getaddrinfo(nullptr, sport.c_str(), &hints, &addressinfo);
    if (result != 0) {
        printf("Error at server socket(): %ld\n", WSAGetLastError());
        return false;
    }

    SOCKET ListenSocket = socket(addressinfo->ai_family, addressinfo->ai_socktype, addressinfo->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("Error at server socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(addressinfo);
        return false;
    }

    // Try to put loopback fast path on.
    bool value;
    DWORD dwBytesRet;
    int status =
        WSAIoctl(
            ListenSocket,
            SIO_LOOPBACK_FAST_PATH,
            &value,
            sizeof(bool),
            NULL,
            0,
            &dwBytesRet,
            0,
            0);

    if (status == SOCKET_ERROR) {
        DWORD LastError = ::GetLastError();
        if (LastError == WSAEOPNOTSUPP) {
            printf("server call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
        }
    }

    // Setup the TCP listening socket
    result = bind(ListenSocket, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
    if (result == SOCKET_ERROR) {
        printf("server bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(addressinfo);
        closesocket(ListenSocket);
        return false;
    }
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
        printf("Listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        return false;
    }

    // Accept a client socket
    SOCKET ClientSocket = accept(ListenSocket, nullptr, nullptr);
    if (ClientSocket == INVALID_SOCKET) {
        printf("server accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        return false;
    }

    std::vector<uint8_t> buffer;
    buffer.resize(blocksize);

    size_t total = 0;

    // Receive until the peer shuts down the connection
    do {
        size_t currentSize = blocksize;
        size_t next = total + currentSize;
        if (next > size)
        {
            currentSize -= next - size;
        }

        result = recv(ClientSocket, (char*)buffer.data(), (int)currentSize, 0);
        if (result > 0)
        {
            total += result;
        }
        else if (result == 0)
        {
            printf("server Connection closing...\n");
            return false;
        }
        else {
            printf("server recv failed: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            return false;
        }
    } while (total < size);

    result = shutdown(ClientSocket, SD_SEND);
    if (result == SOCKET_ERROR) {
        printf("server shutdown failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        return false;
    }
    // cleanup
    closesocket(ClientSocket);

    return true;
}

和测试程序本身是:

int main()
{
    int width = 1920;
    int height = 1080;
    const size_t totalBpp = 3;
    const size_t totalSize = width * height * totalBpp;
    size_t port = 27140;

    size_t times = 1000;
    size_t expectedData = totalSize * times;

    // Initialize Winsock
    int iResult;
    WSADATA wsaData;
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        std::cout << "WSAStartup failed: " << iResult << std::endl;
        return EXIT_FAILURE;
    }

    std::atomic_bool serverOk{ false };
    std::atomic_bool clientOk{ false };

    auto start = std::chrono::high_resolution_clock::now();

    std::thread server = std::thread([&] {
        serverOk = serverReceiveDataWin(expectedData, totalSize, port);
    });

    std::thread client = std::thread([&] {
        clientOk = sendDataWin(expectedData, totalSize, port);
    });
    client.join();
    server.join();
    auto end = std::chrono::high_resolution_clock::now();
    WSACleanup();

    if (!(clientOk && serverOk))
    {
        if (!serverOk) std::cout << "Server was not OK." << std::endl;
        if (!clientOk) std::cout << "Client was not OK." << std::endl;
        return EXIT_FAILURE;
    }

    std::chrono::duration<double> diff = end - start;

    double frameTime = diff.count() / times;
    double fps = 1.0 / frameTime;

    std::cout << "Sent: " << width << "x" << height << "_" << totalBpp << "(" << totalSize << "). times: " << times << std::endl;
    std::cout << "frameTime: " << frameTime << "s." << std::endl;
    std::cout << "fps: " << fps << "." << std::endl;
    std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1048576) << " mebibytes/s." << std::endl;
    std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1000000) << " megabytes/s." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds{ 60 });

    return EXIT_SUCCESS;
}

在我的机器上,我得到以下结果:

Sent: 1920x1080_3(6220800).times : 1000
frameTime : 0.0138217s.
fps : 72.35.
transfer rate : 429.225 mebibytes / s.
transfer rate : 450.075 megabytes / s.

似乎问题出在我的防火墙/防病毒软件上,我首先卸载了F-secure,速度提高到了500MB / s ..卸载并重新启动后,速度提高到了4000MB / s。

2 个答案:

答案 0 :(得分:1)

几点。

  1. 未正确调用IOCTL。

    bool value;
    DWORD dwBytesRet;
    int status =
        WSAIoctl(
            ListenSocket,
            SIO_LOOPBACK_FAST_PATH,
            &value,
            sizeof(bool),
            NULL,
            0,
            &dwBytesRet,
            0,
            0);
    

这应该传入设置为1的DWORD(32位整数)。上面的代码传递从未被初始化为任何内容的C ++ bool(可能只有1个字节)。

  1. 由于这仅是一个套接字,因此它将受到CPU的限制,因为它实际上仅来自用户/内核并在单个线程上进行内存复制。这不太可能在单个连接上达到40 Gbps。特别是因为这仅发送了6GB的数据。

答案 1 :(得分:1)

在发送大量数据时,应关闭Nagle's Algorithm。在发送套接字上设置TCP_NODELAY