Boost Asio async_recieve_from multicast con use_future no funciona simultáneamente con async_send_to

Sep 25 2020

ITNOA

Quiero escribir un programa que envíe y reciba multidifusión simultáneamente como a continuación

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

Mi problema es ¿por qué result.wait () causa para generar una excepción?

La nota quizás importante es que cuando elimino la message_sender.sendlínea, este programa no genera ninguna excepción y línea de result.wait()bloque hasta el final. pero cuando se message_sender.sendagrega la línea , este programa genera una excepción y no funciona correctamente.

Mi message_sender es igual a este código.

Y mi message_receiver es como abajo

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());
}

¿Dónde está mi error?

Pruebo estos códigos en las siguientes plataformas

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

Respuestas

rafix07 Sep 25 2020 at 14:18

Que está recibiendo no_stateporque getel futurepuede ser llamado una sola vez.

Abajo

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

get se llama dos veces.

Ver referencia :

Notas Se recomienda a las implementaciones que detecten el caso cuando valid () sea falso antes de la llamada y arrojen un std :: future_error con una condición de error de std :: future_errc :: no_state.

Después de la primera llamada de get, validvuelve false.