C ++ Boost :: ASIO: системная ошибка 995 после второго вызова io_context :: run - PullRequest
0 голосов
/ 23 мая 2018

У меня проблемы со следующим сценарием, использующим реализацию Windows asio 1.66.0

  1. bind socket
  2. run io_context
  3. stop io_context
  4. закрыть сокет
  5. перезапустить io_context
  6. повторить 1-4

За вызовом io_context :: run во второй итерации следует системная ошибка 995

Операция ввода-вывода была прервана из-за выхода из потока или запроса приложения

Похоже, эта ошибка из-за закрытия сокета: asio использует PostQueuedCompletionStatus / GetQueuedCompletionStatus, чтобы сигнализировать себячто io_context :: stop был вызван.Но операция ввода-вывода, поставленная в очередь WSARecvFrom в socket_.async_receive_from, завершается неудачно из-за того, что сокет закрыт, и при следующем вызове io_context :: run это первое, что я получаю в обработчике, переданном в socket_.async_receive_from.

Предполагается ли поведение asio io_context?Как избежать этой ошибки в последовательных итерациях?

Если я остановлю io_context :: run, закрыв сокет, все работает, за исключением того, что будет та же ошибка, и она выглядит немного грязной.Еще одна странная вещь: если я продолжу работу с do_receive после получения ошибки, я получу столько же ошибок, сколько и количество предыдущих итераций, а затем получу данные из сокета.

// based on boost_asio/example/cpp11/multicast/receiver.cpp
// https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/example/cpp11/multicast/receiver.cpp

#include <array>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <future>
#include <chrono>
#include <thread>

using namespace std::chrono_literals;

constexpr short multicast_port = 30001;

class receiver
{
public:
    explicit receiver(boost::asio::io_context& io_context) : socket_(io_context)
    {}

    ~receiver()
    {
        close();
    }

    void open(
        const boost::asio::ip::address& listen_address,
        const boost::asio::ip::address& multicast_address)
    {
        // Create the socket so that multiple may be bound to the same address.
        boost::asio::ip::udp::endpoint listen_endpoint(
            listen_address, multicast_port);
        socket_.open(listen_endpoint.protocol());
        socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
        socket_.bind(listen_endpoint);

        // Join the multicast group.
        socket_.set_option(
            boost::asio::ip::multicast::join_group(multicast_address));

        do_receive();
    }

    void close()
    {
        if (socket_.is_open())
        {
            socket_.close();
        }
    }

private:
    void do_receive()
    {
        socket_.async_receive_from(
            boost::asio::buffer(data_), sender_endpoint_,
            [this](boost::system::error_code ec, std::size_t length)
            {
                if (!ec)
                {
                    std::cout.write(data_.data(), length);
                    std::cout << std::endl;

                    do_receive();
                }
                else
                {
                    // A call to io_context::run in second iteration is followed by system error 995
                    std::cout << ec.message() << std::endl;
                }
            });
    }

    boost::asio::ip::udp::socket socket_;
    boost::asio::ip::udp::endpoint sender_endpoint_;
    std::array<char, 1024> data_;
};

int main(int argc, char* argv[])
{
    try
    {
        const std::string listen_address = "0.0.0.0";
        const std::string multicast_address = "239.255.0.1";

        boost::asio::io_context io_context;
        receiver r(io_context);
        std::future<void> fut;

        for (int i = 5; i > 0; --i)
        {
            io_context.restart();
            r.open(
                boost::asio::ip::make_address(listen_address),
                boost::asio::ip::make_address(multicast_address));
            fut = std::async(std::launch::async, [&](){ io_context.run(); });
            std::this_thread::sleep_for(3s);
            io_context.stop();
            fut.get();
            r.close();
        }

    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}
...