Boost Asio async_recieve_from multicast com use_future não funciona simultaneamente com async_send_to

Sep 25 2020

ITNOA

Quero escrever programas que enviem e recebam multicast simultaneamente como abaixo

int main()
{
    const string group_address = "235.127.1.1";
    constexpr uint16_t PORT_NUMBER = 8765;

    boost::asio::io_context io_context;
    auto work_guard = boost::asio::make_work_guard(io_context);

    BoostMessageSender message_sender(group_address, "eth1", PORT_NUMBER, io_context);
    BoostMessageReceiver message_receiver(group_address, "eth1", PORT_NUMBER, io_context);

    std::thread t1([&io_context]()
        {
            cout << __LINE__ << endl;
            io_context.run();
        });

    this_thread::sleep_for(1s);

    std::array<char, 20> data = {};
    auto result = std::async(std::launch::async, [&message_receiver, &data]()
        {
                message_receiver.receive(gw::buffer(data));
                cout << " receive finished" << endl; // does not reach this line :(((
        });

    this_thread::sleep_for(1s);
    message_sender.send("Any body there!");

    result.wait(); // Raise exception what(): std::future_error: No associated state
    cout << "receive message: ";
    std::copy(std::begin(data),
          std::end(data),
          std::ostream_iterator<char>(std::cout, ""));

    io_context.stop();
    t1.join();
    return 0;
}

Meu problema é por que result.wait () causa para gerar uma exceção?

a nota talvez importante é que quando eu removo message_sender.sendlinha, este programa não levanta nenhuma exceção e linha de result.wait()bloco até o final. mas quando a linha é message_sender.sendadicionada, este programa levanta uma exceção e não funciona corretamente.

Meu message_sender é igual a este código.

E meu message_receiver é como abaixo

BoostMessageReceiver::BoostMessageReceiver(const std::string& group_address, const std::string& interface_name, uint16_t port_number, ba::io_context& io_context)
    : AbstractMessageReceiver(group_address, interface_name, port_number)
    , io_context(io_context)
    , listen_endpoint(ba::ip::address_v4::any(), port_number)
    , socket(io_context)
{
    socket.open(listen_endpoint.protocol());
    socket.set_option(ba::ip::udp::socket::reuse_address(true));
    socket.bind(listen_endpoint);

    // Join to multicast group
    socket.set_option(ba::ip::multicast::enable_loopback(true));
    socket.set_option(ba::ip::multicast::join_group(ba::ip::address::from_string(group_address)));
}

std::pair<bool, std::size_t> BoostMessageReceiver::receive(const AbstractMessageReceiver::MutableBuffer& buffer) noexcept
{
    ba::ip::udp::endpoint sender_endpoint;

    std::future<std::size_t> result = socket.async_receive_from(ba::buffer(buffer.data(), buffer.size()), sender_endpoint, ba::use_future);

    return std::make_pair(result.get() != 0, result.get());
}

Onde está o meu erro?

Eu testo esse código nas plataformas abaixo

Visual Studio 2019 16.7.4
Windows 10 1909 latest update
Boost 1.73.0

Ubuntu 20.04.1 LTS
GCC 9.3.0
Boost 1.71.0

Respostas

rafix07 Sep 25 2020 at 14:18

Você está conseguindo no_stateporque geton futurepode ser chamado apenas uma vez.

Abaixo

std::make_pair(result.get() != 0, result.get());

get é chamado duas vezes.

Veja a referência :

Notas As implementações são encorajadas a detectar o caso quando valid () é falso antes da chamada e lançar um std :: future_error com uma condição de erro de std :: future_errc :: no_state.

Após a primeira chamada de get, validretorna false.