簡體   English   中英

Boost Asio 和 Udp Poll() 沒有傳入數據

[英]Boost Asio and Udp Poll() No incoming data

我必須以每秒 100 毫秒的速度並行處理來自 100 個端口的信息。

我正在使用 Ubuntu 操作系統。

我做了一些研究,發現poll() function 是一個很好的候選者,以避免打開 100 個線程來處理來自 udp 協議的並行數據。

我主要使用 boost 並嘗試將 poll() 與 boost 集成。

問題是當我試圖通過客戶端數據發送到服務器時,我什么也沒收到。 根據 wireshark,數據正在正確的主機上。 (本地主機,端口 1234)

我錯過了什么還是我放錯了什么?

在此處輸入圖像描述

測試代碼(服務器)

#include <deque>
#include <iostream>
#include <chrono>
#include <thread>

#include <sys/poll.h>

#include <boost/optional.hpp>
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>


using boost::asio::ip::udp;
using namespace boost::asio;

using namespace std::chrono_literals;

std::string ip_address = "127.0.0.1";

template<typename T, size_t N>
size_t arraySize( T(&)[N] )
{
    return(N);
}

class UdpReceiver
{
    using Resolver   = udp::resolver;
    using Sockets    = std::deque<udp::socket>;
    using EndPoint   = udp::endpoint;
    using Buffer     = std::array<char, 100>; // receiver buffer

public:
    explicit UdpReceiver()
            : work_(std::ref(resolver_context)), thread_( [this]{ resolver_context.run(); })
    { }

    ~UdpReceiver()
    {
        work_ = boost::none; // using work to keep run active always !
        thread_.join();
    }

    void async_resolve(udp::resolver::query const& query_) {
        resolver_context.post([this, query_] { do_resolve(query_); });
    }

    // callback for event-loop in main thread
    void run_handler(int fd_idx) {
        // start reading
        auto result = read(fd_idx, receive_buf.data(), sizeof(Buffer));
        // increment number of received packets
        received_packets = received_packets + 1;
        std::cout << "Received bytes " << result << " current recorded packets " << received_packets <<'\n';

        // run handler posted from resolver threads
        handler_context.poll();
        handler_context.reset();
    }

    static void handle_receive(boost::system::error_code error, udp::resolver::iterator const& iterator) {
        std::cout << "handle_resolve:\n"
                     "  " << error.message() << "\n";
        if (!error)
            std::cout << "  " << iterator->endpoint() << "\n";
    }

    // get current file descriptor
    int fd(size_t idx)
    {
        return sockets[idx].native_handle();
    }

private:

    void do_resolve(boost::asio::ip::udp::resolver::query const& query_) {

        boost::system::error_code error;
        Resolver resolver(resolver_context);
        Resolver::iterator result = resolver.resolve(query_, error);

        sockets.emplace_back(udp::socket(resolver_context, result->endpoint()));

        // post handler callback to service running in main thread
        resolver_context.post(boost::bind(&UdpReceiver::handle_receive, error, result));
    }

private:
    Sockets sockets;
    size_t received_packets = 0;
    EndPoint remote_receiver;
    Buffer receive_buf {};

    io_context resolver_context;
    io_context handler_context;
    boost::optional<boost::asio::io_context::work> work_;
    std::thread thread_;
};

int main (int argc, char** argv)
{
    UdpReceiver udpReceiver;
    udpReceiver.async_resolve(udp::resolver::query(ip_address, std::to_string(1234)));

    //logic
    pollfd fds[2] { };
    for(int i = 0; i < arraySize(fds); ++i)
    {
        fds[i].fd = udpReceiver.fd(0);
        fds[i].events = 0;
        fds[i].events |= POLLIN;
        fcntl(fds[i].fd, F_SETFL, O_NONBLOCK);
    }

    // simple event-loop
    while (true) {
        if (poll(fds, arraySize(fds), -1)) // waiting for wakeup call. Timeout - inf
        {
            for(auto &fd : fds)
            {
                if(fd.revents & POLLIN) // checking if we have something to read
                {
                    fd.revents = 0; // reset kernel message
                    udpReceiver.run_handler(fd.fd); // call resolve handler. Do read !
                }
            }
        }
    }
    return 0;
}

這看起來像是 C 樣式poll代碼和 Asio 代碼的混淆組合。 重點是

  • 你不需要輪詢(Asio 在內部進行(或 epoll/select/kqueue/IOCP - 任何可用的)
  • UDP 是無連接的,因此您不需要超過一個套接字即可接收所有“連接”(發送方)

我將在單個線程上用單個udp::socket替換它。 您甚至不必管理線程/工作:

net::thread_pool io(1); // single threaded
udp::socket s{io, {{}, 1234}};

讓我們運行一個異步接收循環 5 秒:

std::array<char, 100> receive_buffer;
udp::endpoint sender;

std::function<void(error_code, size_t)> read_loop;
read_loop = [&](error_code ec, size_t bytes) {
    if (bytes != size_t(-1)) {
        //std::cout << "read_loop (" << ec.message() << ")\n";
        if (ec)
            return;

        received_packets += 1;
        unique_senders.insert(sender);
        //std::cout << "Received:" << bytes << " sender:" << sender << " recorded:" << received_packets << "\n";
        //std::cout << std::string_view(receive_buffer.data(), bytes) << "\n";
    }
    s.async_receive_from(net::buffer(receive_buffer), sender, read_loop);
};

read_loop(error_code{}, -1); // prime the async pump

// after 5s stop
std::this_thread::sleep_for(5s);
post(io, [&s] { s.cancel(); });

io.join();

最后,我們可以報告統計信息:

std::cout << "A total of " << received_packets << " were received from "
          << unique_senders.size() << " unique senders\n";

使用bash中的模擬負載:

function client() { while read a; do echo "$a" > /dev/udp/localhost/1234 ; done < /etc/dictionaries-common/words; }

for a in {1..20}; do client& done; time wait

我們得到:

A total of 294808 were received from 28215 unique senders

real    0m5,007s
user    0m0,801s
sys     0m0,830s

在此處輸入圖像描述

這顯然沒有優化,這里的瓶頸可能是為客戶推出的許多bash 子外殼。

完整列表

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
#include <set>

namespace net = boost::asio;
using boost::asio::ip::udp;
using boost::system::error_code;
using namespace std::chrono_literals;

int main ()
{
    net::thread_pool io(1); // single threaded
    udp::socket s{io, {{}, 1234}};

    std::set<udp::endpoint> unique_senders;
    size_t                  received_packets = 0;

    {
        std::array<char, 100> receive_buffer;
        udp::endpoint sender;

        std::function<void(error_code, size_t)> read_loop;
        read_loop = [&](error_code ec, size_t bytes) {
            if (bytes != size_t(-1)) {
                //std::cout << "read_loop (" << ec.message() << ")\n";
                if (ec)
                    return;

                received_packets += 1;
                unique_senders.insert(sender);
                //std::cout << "Received:" << bytes << " sender:" << sender << " recorded:" << received_packets << "\n";
                //std::cout << std::string_view(receive_buffer.data(), bytes) << "\n";
            }
            s.async_receive_from(net::buffer(receive_buffer), sender, read_loop);
        };

        read_loop(error_code{}, -1); // prime the async pump

        // after 5s stop
        std::this_thread::sleep_for(5s);
        post(io, [&s] { s.cancel(); });

        io.join();
    }

    std::cout << "A total of " << received_packets << " were received from "
              << unique_senders.size() << " unique senders\n";
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM