Boost::Asio: asynchrones Empfangen funktioniert nur manchmal: Timing problem?



  • Servus,

    ich portiere (mal wieder) native Sockets nach boost::asio.

    Zu diesem Zweck habe ich mir eine kleine Klasse anhand dieses Beispiels: http://www.boost.org/doc/libs/1_40_0/doc/html/boost_asio/example/multicast/receiver.cpp
    geschrieben, die async_receive_from benutzt.

    Es gibt eine Manager-Klasse, die den io_service, den Thread, und ein Objekt der Receiver-Klasse besitzt.

    class Manager{
    public:
        Manager();
        ~Manager();
    private:
        void run();
    
        boost::asio::io_service m_io_service;
    
        std::auto_ptr<Receiver> m_receiver;
    
        boost::thread m_io_thread;
    
        std::auto_ptr<boost::asio::io_service::work> m_io_work;
    };
    
    Manager::Manager():
            m_io_service(),
            m_receiver(new Receiver(m_io_service, ip_addr, port),
            m_io_thread(boost::bind(&Manager::run, this)),
            m_io_work(new boost::asio::io_service::work(m_io_service))
    {}
    
    Manager::~Manager()
    {
        m_io_work.reset();
        m_io_thread.join();
    }
    
    void Manager::run()
    {
        m_io_service.run();
    }
    

    Der Konstruktor der Receiver-Klasse ruft, wie im verlinkten Beispiel zu sehen, async_recv_from auf, und der davon aufgerufene Handler ruft wiederum async_recv_from auf.

    So weit, so schön.

    Jetzt habe ich zweites kleines Programm, das zu Testzwecken alle Sekunde ein Paket sendet.

    Und jetzt das Problem: Mein Empfängerprogramm empfängt die Paket auch, aber nur manchmal. Und zwar abhängig wohl davon, wann es gestartet wird:
    Beispiel: Von zehn Programmstarts empfängt es neun mal überhaupt nichts, aber wenn es einmal beim Start was empfängt, empfängt es fürderhin alle Pakete.

    Dieses "zufällige" Verhalten sieht mir irgendwie nach einem Timing-Problem aus, kann es sein, dass wenn beim ersten Aufruf von async_recv nix zu tun ist, der io_service.run zurückkehrt ? Aber gerade das wird doch laut http://www.boost.org/doc/libs/1_40_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work durch work verhindert?

    Ich steh echt auf'm Schlauch.


  • Administrator

    Zeig doch mal den auf das wesentliche reduzierte Code für das Senden und für das Empfangen. Ich meine, dass sind doch die entscheidenden Codestücke! Mit deinen Informationen hier, welche du uns gegeben hast, können wir nur raten.

    Grüssli



  • Hier der Rest vom Code

    int main()
    {
    }
    


  • Das ist der Receiver:

    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    
    typedef boost::asio::ip::udp::endpoint UDPEndpoint;
    typedef boost::asio::ip::udp::socket UDPSocket;
    
    class Receiver
    {
    public:
        Receiver(boost::asio::io_service& io_service, const boost::asio::ip::address& multicast_address, short multicast_port);
    
       void handle_receive_from(const boost::system::error_code& error,
                                 std::size_t bytes_recvd);
    
    private:
    
        UDPEndpoint m_endpoint;
    
        UDPSocket m_socket;
    
        enum { max_length = 1440 };
    
        char data_[max_length];
    
    };
    
    Receiver::Receiver(boost::asio::io_service& io_service,const boost::asio::ip::address& multicast_address, short multicast_port)
    : m_socket(io_service)
    {
        UDPEndpoint listen_endpoint(multicast_address, multicast_port);
        m_socket.open(listen_endpoint.protocol());
    
        // REUSE PORT bzw. REUSE ADDR
        m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
        m_socket.bind(listen_endpoint);
    
        // Join the multicast group.
        m_socket.set_option(boost::asio::ip::multicast::join_group(multicast_address));
    
        m_socket.async_receive_from(boost::asio::buffer(data_, max_length),
                                    m_endpoint,
                                    boost::bind(&Receiver::handle_receive_from,
                                                this,
                                                boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred)
                                    );
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////
    
    void Receiver::handle_receive_from(const boost::system::error_code& error,
                                                               std::size_t bytes_recvd)
    {
        if(error)
        {
            std::cout << "Async receive failed with: " << error.message() << std::endl;
        }
        else
        {
            std::cout.write(data_,bytes_recvd);
    
        m_socket.async_receive_from(boost::asio::buffer(data_, max_length),
                                    m_endpoint,
                                    boost::bind(&Receiver::handle_receive_from,
                                                this,
                                                boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred)
                                    );
        }
    }
    

    EDIT: Was soll denn das für ein Scherzposting sein?



  • Zu erwähnen ist noch, dass das run des threads in jedem Fall gestartet wird, das habe ich überprüft.

    Der Sender ist ebenfalls nicht das Problem, der läuft seit Monaten zuverlässig und arbeitet mit dem auf nativen Unix-Sockets basierenden receiver problemlos zusammen.



  • Dumm? Was sehen wird jetzt? Was ist AsynchronousUDPMulticastReceiver?



  • Beim Posten hab ich den Namen vergessen zu editieren... war wohl etwas in Eile. Hab den Post editiert.

    Es sind nur die zwei Klassen beteiligt.

    Ich erzeuge eine Instanz des managers, der stößt den Thread an (kann man mittels einer Ausgabe sehen), und in 90% der Fälle passiert dann nix.

    Und könntest du bitte unter einem anderen Namen posten, das verwirrt mich 🙂


Anmelden zum Antworten