草庐IT

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

coder 2023-09-19 原文

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

我首先尝试使用 boost::asio 库实现它。一切正常,除了吞吐量非常慢,415 兆字节/秒。

然后我开始用 winsock 创建一个测试用例,它也有非常相似的吞吐量,434 兆字节/秒。非常慢。

我原本期望在 40 GB 或每秒至少几 GB 的范围内有更多数据。

我很感激任何建议,因为我不擅长网络编程。

我当前的客户端功能:

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,速度增加到 500 兆字节/秒。卸载并重新启动后,速度增加到 4000 兆字节/秒。

最佳答案

几点。

  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 的数据。

关于C++ 低吞吐量 winsock TCP 测试应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52000333/

有关C++ 低吞吐量 winsock TCP 测试应用程序的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  6. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  7. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  8. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  9. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  10. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

随机推荐