2017-06-08 8 views
1

Ich verwende BOOST für die asynchrone Kommunikation mit einer seriellen Schnittstelle. Ich kann die Ursache des Irrtums, mit dem ich konfrontiert bin, nicht genau bestimmen und würde mich über einige Hinweise freuen.Warum verursacht meine Implementierung von io_service :: run_one() einen unbestimmten Block und löst den Fehler # 125 aus?

std::string myclass::readStringUntil(const std::string& delim) 
{ 
    setupParameters=ReadSetupParameters(delim); 
    performReadSetup(setupParameters); 

if(timeout!=posix_time::seconds(0)) timer.expires_from_now(timeout); 
else timer.expires_from_now(posix_time::hours(100000)); 

timer.async_wait(boost::bind(&myclass::timeoutExpired,this, 
      asio::placeholders::error)); 

result=resultInProgress; 
bytesTransferred=0; 
for(;;) 
{ 
    io.run_one(); 
    switch(result) 
    { 
     case resultSuccess: 
      { 
       timer.cancel(); 
       bytesTransferred-=delim.size();//Don't count delim 
       istream is(&readData); 
       string result(bytesTransferred,'\0');//Alloc string 
       is.read(&result[0],bytesTransferred);//Fill values 
       is.ignore(delim.size());//Remove delimiter from stream 
       return result; 
      } 
     case resultTimeoutExpired: 
      port.cancel(); 
      throw(timeout_exception("Timeout expired")); 
      cout<<"timeout on readuntill"<<endl; 
     case resultError: 
      timer.cancel(); 
      port.cancel(); 
      throw(boost::system::system_error(boost::system::error_code(), 
        "Error while reading")); 
    } 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::performReadSetup(const ReadSetupParameters& param) 
{ 
if(param.fixedSize) 
{ 
    asio::async_read(port,asio::buffer(param.data,param.size),boost::bind(
      &myclass::readCompleted,this,asio::placeholders::error, 
      asio::placeholders::bytes_transferred)); 
} else { 
    asio::async_read_until(port,readData,param.delim,boost::bind(
      &myclass::readCompleted,this,asio::placeholders::error, 
      asio::placeholders::bytes_transferred)); 
} 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::timeoutExpired(const boost::system::error_code& error) 
{ 
if(!error && result==resultInProgress) result=resultTimeoutExpired; 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::readCompleted(const boost::system::error_code& error, 
    const size_t bytesTransferred) 
{ 
if(!error) 
{ 
    result=resultSuccess; 
    this->bytesTransferred=bytesTransferred; 
    return; 
} 

#ifdef _WIN32 
if(error.value()==995) return; //Windows spits out error 995 
#elif defined(__APPLE__) 
if(error.value()==45) 
{ 
    //Bug on OS X, it might be necessary to repeat the setup 
    //http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html 
    performReadSetup(setupParameters); 
    return; 
} 
#else //Linux 
if(error.value()==125) return; //Linux outputs error 125 
#endif 

result=resultError; 
} 

Ohne io.run_one(), gehe ich in eine Endlosschleife und nicht in den Schalter Fall.

Wie konnte ich meinen Code reparieren, so dass er aus dem unbestimmten Block herauskommt? Ich kann nicht bestätigen, aber ich denke, die run_one() verursacht einen Fehler # 125

Antwort

0

Zunächst ist Fehler 125 Operation abgebrochen: so würde das bedeuten (wahrscheinlich) eine cancel() -Aufruf (oder der Destruktor der Dieses Objekt verursacht eine Stornierung.

Das ist normal.

Ich habe painstakenly abgeschlossen Ihre unvollständige code¹ und sehen nicht leicht Ihr Problem:

Live On Coliru

#include <boost/asio.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

struct myclass { 
    struct timeout_exception : std::runtime_error { 
     timeout_exception(std::string const &msg) : std::runtime_error(msg) {} 
    }; 

    enum { 
     resultInProgress, 
     resultTimeoutExpired, 
     resultSuccess, 
     resultError, 
    } result = resultInProgress; 

    std::string readStringUntil(std::string const &); 
    struct ReadSetupParameters { 
     ReadSetupParameters(std::string const &d = "") : delim{ d } {} 
     std::string delim; 
     bool fixedSize = false; 
     char mutable data[1024]; 
     size_t size = sizeof(data); 
    }; 

    void performReadSetup(const ReadSetupParameters &param); 

    ReadSetupParameters setupParameters; 
    boost::posix_time::time_duration timeout{ boost::posix_time::seconds(3) }; 
    boost::asio::io_service io; 
    boost::asio::deadline_timer timer{ io }; 

    // more likely a serial port, but I'm not gonna bother mocking that: 
    boost::asio::ip::tcp::socket port{ io }; 
    boost::asio::streambuf readData; 
    size_t bytesTransferred; 

    myclass() { port.connect({ {}, 6767 }); } 

    void timeoutExpired(boost::system::error_code const &ec); 
    void readCompleted(boost::system::error_code const &ec, size_t bytesTransferred); 
}; 

std::string myclass::readStringUntil(const std::string &delim) { 
    using namespace boost; 

    setupParameters = ReadSetupParameters(delim); 
    performReadSetup(setupParameters); 

    if (timeout != posix_time::seconds(0)) 
     timer.expires_from_now(timeout); 
    else 
     timer.expires_from_now(posix_time::hours(100000)); 

    timer.async_wait(boost::bind(&myclass::timeoutExpired, this, asio::placeholders::error)); 

    result = resultInProgress; 
    for (;;) { 
     io.run_one(); 
     switch (result) { 
     case resultSuccess: { 
      timer.cancel(); 
      bytesTransferred -= delim.size(); // Don't count delim 
      std::istream is(&readData); 
      std::string result(bytesTransferred, '\0'); // Alloc string 
      is.read(&result[0], bytesTransferred);  // Fill values 
      is.ignore(delim.size());     // Remove delimiter from stream 
      return result; 
     } break; 
     case resultTimeoutExpired: 
      port.cancel(); 
      std::cout << "timeout on readuntill" << std::endl; 
      throw(timeout_exception("Timeout expired")); 
      break; 
     case resultError: 
      timer.cancel(); 
      port.cancel(); 
      throw(boost::system::system_error(boost::system::error_code(), "Error while reading")); 
     } 
    } 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::performReadSetup(const ReadSetupParameters &param) { 
    using namespace boost; 
    if (param.fixedSize) { 
     asio::async_read(port, asio::buffer(param.data, param.size), 
         boost::bind(&myclass::readCompleted, this, asio::placeholders::error, 
            asio::placeholders::bytes_transferred)); 
    } else { 
     asio::async_read_until(port, readData, param.delim, 
           boost::bind(&myclass::readCompleted, this, asio::placeholders::error, 
              asio::placeholders::bytes_transferred)); 
    } 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::timeoutExpired(const boost::system::error_code &error) { 
    if (!error && result == resultInProgress) 
     result = resultTimeoutExpired; 
} 

///////////////////////////////////////////////////////////////////////////// 

void myclass::readCompleted(const boost::system::error_code &error, const size_t bytesTransferred) { 
    if (!error) { 
     result = resultSuccess; 
     this->bytesTransferred = bytesTransferred; 
     return; 
    } 

#ifdef _WIN32 
    if (error.value() == 995) 
     return; // Windows spits out error 995 
#elif defined(__APPLE__) 
    if (error.value() == 45) { 
     // Bug on OS X, it might be necessary to repeat the setup 
     // http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html 
     performReadSetup(setupParameters); 
     return; 
    } 
#else // Linux 
    if (error.value() == 125) 
     return; // Linux outputs error 125 
#endif 

    result = resultError; 
} 

int main() { 
    myclass absent; 
    std::cout << "Ok: '" << absent.readStringUntil("Transferred") << "'\n"; 
} 

Hinweise:

  • es, als ob man schaut Ich versuche grundsätzlich wirklich, asynch Calls überhaupt zu vermeiden. Das macht Dinge ungeschickt. Wenn alles, was Sie brauchen das Timeout, siehe Boost::Asio synchronous client with timeout und boost::asio + std::future - Access violation after closing socket
  • Sie nicht bewusst scheint, dass *read_untilüber das Trennzeichen lesen kann (es wird mindestens bis lesen und einschließlich des ersten Mal das Trennzeichen sieht). Sie sollten es wirklich berücksichtigen
  • Sie überprüfen nie den Rückgabewert für run_one(). Wenn es 0 zurückgibt, sollte die Schleife beendet werden. Wenn Sie es erneut ausführen, ohne eine reset() durchzuführen, werden Sie nie etwas tun.

¹ warum?

+0

Hey! Danke, dass du den Überblick behältst und mir hilfst! Ich entschuldige mich, wenn ich Ihnen einen unvollständigen Code übergeben habe. Es ist ein Ausschnitt aus dem Original. Ich bekomme "Fehler beim Lesen" auf meiner Konsole. Was hat io.run_one() in diesem Fall ausgeführt? Wie hat sich result = resultInProgress geändert? Für diejenigen, die in der Zukunft folgen, ist es eine Fortsetzung von dieser [Frage] (https://stackoverflow.com/questions/44429978/how-to-use-boostasioio-servicerun-one/44430221#44430221) –

+0

Hast du gerade versucht zu debuggen? Oder fügen Sie ein Tracing in den Handlern 'readCompleted' oder' timeoutExpired' hinzu? Wie Sie sehen können, bekomme ich [nicht das spezifische Verhalten] (http://coliru.stacked-crooked.com/a/04ed1dcf723ff2b5). Achten Sie darauf, alle Hinweise unter meinem Antwortcode zu beachten. – sehe

+0

Ich werde versuchen, es zu debuggen. Das Einrichten von SublimeGDB und der Projektdatei/Einstellungen ist wirklich sehr verwirrend, aber ich werde beharren. Vielen Dank für Ihre Hilfe –

Verwandte Themen