[gelöst] boost::asio synchrones Versenden



  • Hallo zusammen,

    ich versuche mich (mal wieder) mit boost::asio und habe folgendes Problem:
    Ich möchte synchrones TCP/IP machen, dazu habe ich mir diesen Beispielquelltext angeguckt und nach meinen Vorstellungen angepasst:

    Header:

    #ifndef SynchronousTCPClientH
    #define SynchronousTCPClientH
    
    #include <string>
    
    #include <boost/noncopyable.hpp>
    #include <boost/asio/ip/tcp.hpp>
    #include <boost/asio/io_service.hpp>
    #include <boost/asio/deadline_timer.hpp>
    
    class SynchronousTCPClient : boost::noncopyable
    {
    	bool				 Connected_ = false;
    	boost::asio::io_service          Service_;
    	boost::asio::ip::tcp::socket	 Socket_;
    	boost::asio::deadline_timer	 DeadlineTimer_;
    	boost::posix_time::time_duration Timeout_ = boost::posix_time::seconds( 10 );
    public:
      SynchronousTCPClient();
    
       void connect( std::string const& host, unsigned short port );
       void disconnect();
    
       bool connected() const;
    
       void  send_telegram( std::string const& data );
       std::string receive_telegram();
    private:
       void check_deadline();
       void send_raw( void const* data, std::size_t length );
       void execute_operation( boost::system::error_code& ec );
    };
    #endif
    

    Implementierung:

    #pragma hdrstop
    
    #include <stdexcept>
    #include <boost/lambda/bind.hpp>
    #include <boost/lambda/lambda.hpp>
    #include <boost/asio/write.hpp>
    #include <boost/asio/connect.hpp>
    #include <boost/system/error_code.hpp>
    
    #include "common/io/SynchronousTCPClient.h"
    
    SynchronousTCPClient::SynchronousTCPClient() :
       Socket_( Service_ ),
       DeadlineTimer_( Service_ )
    {
       DeadlineTimer_.expires_at( boost::posix_time::pos_infin );
       check_deadline();
    }
    
    void SynchronousTCPClient::connect( std::string const& host, unsigned short port )
    {
       disconnect();
    
       std::string const service = std::to_string( port );
    
       boost::asio::ip::tcp::resolver::query const query( host, service );
       boost::asio::ip::tcp::resolver::iterator iter = boost::asio::ip::tcp::resolver( Service_ ).resolve( query );
    
       DeadlineTimer_.expires_from_now( Timeout_ );
       boost::system::error_code ec = boost::asio::error::would_block;
       boost::asio::async_connect( Socket_, iter, boost::lambda::var( ec ) = boost::lambda::_1 );
       execute_operation( ec );
    
       Connected_ = true;
    }
    
    void SynchronousTCPClient::disconnect()
    {
       if( Connected_ )
       {
          Connected_ = false;
    
          boost::system::error_code ignored_ec;
          Socket_.close( ignored_ec );
          DeadlineTimer_.expires_at( boost::posix_time::pos_infin );
       }
    }
    
    bool SynchronousTCPClient::connected() const
    {
       return Connected_;
    }
    
    void SynchronousTCPClient::send_telegram( std::string const& data )
    {
       // Daten werden mit 32bit Längenpräfix versendet
       std::uint32_t const data_length = data.size();
       send_raw( &data_length, sizeof( data_length ) );
       if( data_length > 0 )
       {
          send_raw( &data[0], data_length );
       }
    }
    
    void SynchronousTCPClient::send_raw( void const* data, std::size_t length )
    {
       if( connected() && data && length > 0 )
       {
          DeadlineTimer_.expires_from_now( Timeout_ );
          boost::system::error_code ec = boost::asio::error::would_block;
          boost::asio::async_write( Socket_, boost::asio::buffer( data, length ), boost::lambda::var( ec ) == boost::lambda::_1 );
          execute_operation( ec );
       }
    }
    
    std::string SynchronousTCPClient::receive_telegram()
    {
       return std::string();
    }
    
    void SynchronousTCPClient::execute_operation( boost::system::error_code& ec )
    {
       do
       {
          Service_.run_one();
       }
       while( ec == boost::asio::error::would_block );
       if( ec ) throw std::runtime_error( "to do" );
       if( !Socket_.is_open() ) throw std::runtime_error( "to do" );
    }
    
    void SynchronousTCPClient::check_deadline()
    {
       if( DeadlineTimer_.expires_at() <= boost::asio::deadline_timer::traits_type::now() )
       {
          disconnect();
       }
       DeadlineTimer_.async_wait( boost::lambda::bind( &SynchronousTCPClient::check_deadline, this ) );
    }
    

    Driver:

    #include "common/io/SynchronousTCPClient.h"
    
    int main()
    {
       SynchronousTCPClient client;
       client.connect( "localhost", 4711 );
       client.send_telegram( "Hello World" );
    }
    

    So weit, so gut. Der Verbindungsaufbau funktioniert, die Gegenstelle meldet, dass sich jemand mit ihr verbunden hat. Wenn ich jedoch Daten versenden möchte passiert das hier:

    1. send_telegram( "Hello World" ) wird aufgerufen
    2. send_raw( xyz, 4 ) wird aufgerufen (Zeiger auf 32bit uint, Länge 4 Byte)
    3. asynchrone I/O Operation wird vorbereitet (.cpp, Zeilen 71-73)
    4. execute_operation( ec ) wird mit ec = would_block ausgeführt
    5. die do-while Schleife wird durchlaufen und bricht nicht ab. An der Gegenstelle kommt nichts an
    6. check_deadline() wird angesprungen, der Deadline Timer ist abgelaufen. Die Verbindung wird getrennt.
    7. die do-while Schleife macht einen weiteren Durchlauf und bleibt in Service_.run_one(); hängen und kehrt nicht zurück

    Hab da keine Erklärung für, der Quelltext ist noch ziemlich übersichtlich und quasi 1:1 der Quelltext des Beispiels. Hat jemand ne Idee, was hier falsch läuft?

    Edit: Ich benutze boost v1.55 und kann leider nicht updaten.



  • Ich bin echt so doof, dass mich die Schweine beißen, ich hoffe, da hat niemand länger drauf geguckt und Zeit verbrannt.
    In Zeile 71 mache ich einen Vergleich statt einer Zuweisung, deswegen funktioniert der connect, aber nicht das send_telegram.


Anmelden zum Antworten