Legacy Software - Seriell durch boost::asio tcp/ip ersetzen



  • ich versuche gerade eine alte Software mit seriellen Geräte wieder zum laufen zu bekommen - die Geräte fehlen mir - aber ich habe eine TCP/IP basierte Simulation welche exakt die Protokolle unterstützt

    Es gibt RS232 auf TCP/IP Mapper - aber die freien Varianten sind zu stark begrenzt - ich brauch >15 Ports

    Konkret habe ich 3 Funktionen in der alten Software die ich per #define auf
    asio-TCP/IP-Varianten umbiege (damit ich nicht die ganze Protokoll-Verarbeitung anfassen muss - möglichst wenig Änderungen an der alten Software ist mein Ziel)

    1. serial_port::read(buffer, buffer_size, read_size)
    Blockiert (sehr sehr sehr kurz) und liefert einfach nur was auf der seriellen Schnittstelle verfuegbar ist
    (asio/socket -> async_receive?)

    2. serial_port::read_timeout(buffer, read_size, timeout)
    Blockiert (maximal mit timeout) und wartet auf die size an Daten
    (asio/socket -> async_read_some?)

    3. serial_port::write_timeout(buffer, write_size)
    Blockiert (maximal mit timeout) bis bytes geschrieben
    (asio/socket -> async_write_some?)

    wie koennte ich die am besten mit asio als Client nachbauen - hat jemand Tipps?
    ich orientiere mich dabei an dem https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp Beispiel für die Timeouts



  • ist fuer VS2010/C++03, Boost 1.61.0 und es ist nur wichtig das die Timesouts auch anschlagen - ob die Kommunikation schneller oder langsamer abläuft ist nicht relevant



  • Ich baue jetzt erstmal ein verwertbares Beispiel und Frage dann noch mal später



  • VS2010, C++03, Boost 1.61.0
    basiert auf: https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp

    die Routinen
    1. write_n_timeout
    2. read_available
    3. read_n_with_timeout

    Verhalten sich mal so wie ich das gerne hätte - aber:
    -die Fehlerfälle werde noch nicht "sauber" mit return-Code (weil die alten C/C++-Schnittstellen so sind) behandelt (wie komme ich da richtig drann?)
    -ich bin mir mit den boost::lambdas oder handlern unsicher - ist das richtig so oder geht das mit weniger Code/sauberer
    -ich bin bei den async_write/read/receive(mit den cancel) nicht 100% sicher ob das die richtigen Funktionen fuer mich sind

    Fragen sind mit "Frage:" markiert

    zum testen benutzte ich Hercules: http://www.hw-group.com/products/hercules/index_de.html

    #include <boost/asio/connect.hpp>
    #include <boost/asio/deadline_timer.hpp>
    #include <boost/asio/io_service.hpp>
    #include <boost/asio/ip/tcp.hpp>
    #include <boost/asio/streambuf.hpp>
    #include <boost/system/system_error.hpp>
    #include <boost/asio/write.hpp>
    #include <boost/asio/read.hpp>
    #include <boost/bind/bind.hpp>
    #include <boost/asio/placeholders.hpp>
    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include <boost/lambda/bind.hpp>
    #include <boost/lambda/lambda.hpp>
    
    using boost::asio::deadline_timer;
    using boost::asio::ip::tcp;
    using boost::lambda::bind;
    using boost::lambda::var;
    using boost::lambda::_1;
    
    class client
    {
    public:
      client(): socket_(io_service_), deadline_(io_service_)
      {
        deadline_.expires_at(boost::posix_time::pos_infin);
        check_deadline();
      }
    
      void connect(const std::string& host, const std::string& service, boost::posix_time::time_duration timeout)
      {
        tcp::resolver::query query(host, service);
        tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
    
        deadline_.expires_from_now(timeout);
    
        boost::system::error_code ec = boost::asio::error::would_block;
    
        boost::asio::async_connect(socket_, iter, var(ec) = _1);
    
        // Block until the asynchronous operation has completed.
        do
        {
          io_service_.run_one(); 
        }
        while (ec == boost::asio::error::would_block);
    
        if (ec || !socket_.is_open())
        {
          throw boost::system::system_error(ec ? ec : boost::asio::error::operation_aborted);
        }
      }
    
      //warte auf n bytes bis zum timeout
      int read_n_with_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout)
      {
        deadline_.expires_from_now(timeout);
    
        boost::system::error_code ec = boost::asio::error::would_block;
    
        boost::asio::async_read(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1); // async - fuer den timeout  
    
        // Block until the asynchronous operation has completed.
        do
        { 
          io_service_.run_one(); 
        }
        while (ec == boost::asio::error::would_block);
    
        if (ec)
        {
          //throw boost::system::system_error(ec);
          return -1;
        }
    
        //FRAGE: im Timeoutfall kommt "Die Netzwerkverbindung wurde durch das lokale System getrennt" als Exception meldung - kann ich damit sauber einen Timeout erkennen?
    
        return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT
      }
    
      void read_available_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
      {
        if(!error)
        {
          printf("read_available_handler: %u\n", bytes_transferred);
          int brk = 1;
        }
        else if(error == boost::asio::error::operation_aborted)
        {
          //wenn keine Daten + cancel()
          printf("read_handler error: boost::asio::error::operation_aborted\n");
          assert(bytes_transferred == 0);
        }
        else
        {
          printf("read_available_handler error: %s\n", error.message().c_str());
          assert(false);
        }
      }
    
      //lese was da ist - 0-n bytes ohne timeout da nicht blockierend
      int read_available(void* p_data, size_t p_size)
      {
        boost::system::error_code ec;// = boost::asio::error::would_block;
    
        //FRAGE: wie kann man das mit boost::lambda - oder eben C++03 konform schreiben ohne den read_available_handler?
        socket_.async_receive(boost::asio::buffer(p_data, p_size), boost::bind(&client::read_available_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); // async - fuer den timeout  
    
        //hier will auch jemand 0-n bytes lesen - eben was da ist
        //http://stackoverflow.com/questions/31939843/boostasioasync-receive-and-0-bytes-in-socket 
        socket_.cancel(); // abbrechen scheint hier die loesung zu sein um die gleiche semantic zu erreichen - es funktioniert auch, sieht aber komisch aus
    
        //FRAGE: ist das so richtig?
        //do
        //{
        io_service_.run_one(); 
        //}
        //while (ec == boost::asio::error::would_block);
    
        //FRAGE: wie komme ich hier an die transfered bytes?
    
        if (ec)
        {
          //throw boost::system::system_error(ec);
          return -1;
        }
    
        return 0; // 0-n byte gelesen, <0 = FEHLER
      }
    
      //schreibe n bytes (mit timeout)
      int write_n_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout)
      {
        deadline_.expires_from_now(timeout);
    
        boost::system::error_code ec = boost::asio::error::would_block;
    
        boost::asio::async_write(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1);
    
        // Block until the asynchronous operation has completed.
        do
        { 
          io_service_.run_one(); 
        }
        while (ec == boost::asio::error::would_block);
    
        if (ec)
        {
          //throw boost::system::system_error(ec);
          return -1;
        }
    
        //Frage: wie den Timeout sauber erkennen?
    
        return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT
      }
    
    private:
      void check_deadline()
      {
        if (deadline_.expires_at() <= deadline_timer::traits_type::now())
        {
          boost::system::error_code ignored_ec;
          socket_.close(ignored_ec);
    
          deadline_.expires_at(boost::posix_time::pos_infin);
        }
    
        // Put the actor back to sleep.
        deadline_.async_wait(bind(&client::check_deadline, this));
      }
    
      boost::asio::io_service io_service_;
      tcp::socket socket_;
      deadline_timer deadline_;
      boost::asio::streambuf input_buffer_;
    };
    
    //----------------------------------------------------------------------
    
    int main(int argc, char* argv[])
    {
      try
      {
        std::string host = "localhost";
        std::string service = "544";
    
        client c;
        c.connect(host, service, boost::posix_time::seconds(10));
    
        char buffer[10]={0};
    
        printf("write_n_timeout (schreibe n bytes (mit timeout))\n");
        c.write_n_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10));
    
        printf("read_available (lese was da ist - 0-n bytes ohne timeout da nicht blockierend)\n"); // bei dieser implementierung bin ich mir total unsicher
        c.read_available(buffer, sizeof(buffer)); // hol was da ist - nicht blockierend und ohne timeout
    
        printf("read_n_with_timeout (warte auf n bytes bis zum timeout)\n");
        boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time();
        c.read_n_with_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10));
        boost::posix_time::ptime time_received = boost::posix_time::microsec_clock::universal_time();
    
        std::cout << "Round trip time: ";
        std::cout << (time_received - time_sent).total_microseconds();
        std::cout << " microseconds\n";
      }
      catch (std::exception& e)
      {
        std::cerr << "Exception: " << e.what() << "\n";
      }
    
      return 0;
    }
    

Log in to reply