2017-07-09 5 views
0

Ich arbeite an einem Chat. Aus irgendeinem Grund wird die durch Server::msgHandler von Server::msgHandler zusammengesetzte Zeichenfolge an dem Punkt, an dem die Benutzernachricht zwischen anderen Clients verteilt wird, durch async_write in Connection::write zerlegt, wodurch sie so aussieht, als ob nur der Teil dieser Zeichenfolge tatsächlich gelesen wurde. Beispiel:boost :: async_write überspringt Teile der Zeichenfolge

Konstruiert Nachricht: "Hallo Leute von Jack"
Erscheint als: "Jack"

, die die string str=Hello people ist nicht ausgedruckt. Zuerst dachte ich, es wäre mit dem impliziten \0 an seinem Ende zu tun, aber das würde keinen Sinn machen, außerdem, als ich verschiedene Positionen der Zeichenfolge in der Nachricht versuchte ich festgestellt, dass, wenn str mit anderem Text vorangestellt ist, wird der Text gezeigt werden, entweder str vollständig ausstrahlend, oder es an unerwarteten Plätzen platzierend. Z.B.

writeMsg("It was said: \n"+str+" by \"name\"\n");
wird wie folgt angezeigt:
Es wurde gesagt
von "name" Hallo Leute

voll, minimal, übersetzbar Beispiel:

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


#include <iostream> 
#include <vector> 
#include <deque> 

typedef boost::asio::io_service io_service; 
typedef boost::asio::ip::tcp tcp; 

class Server; 

class Connection : public boost::enable_shared_from_this<Connection> { 
    io_service::strand strand; 
    tcp::socket soc; 
    std::deque<std::string> msgBuff; 
    boost::asio::streambuf buf; 
    Server* server; 
    void(Server::*serverHandler)(std::string); 

private: 
    Connection(io_service& service) :soc(service), strand(service){ 

    } 

    void writeStranded(std::string msg){ 
     msgBuff.push_back(msg); 
     if (msgBuff.size() > 1)return; 
     write(); 

    } 
    void write(){ 
     std::string& tmpMsg = msgBuff[0]; 
     boost::asio::async_write(
      soc, 
      boost::asio::buffer(tmpMsg.c_str(), tmpMsg.size()), 
      strand.wrap(
      boost::bind(&Connection::handleWrite, 
       this, 
       boost::asio::placeholders::error, 
       boost::asio::placeholders::bytes_transferred) 
       ) 
     ); 
    } 
    void handleWrite(const boost::system::error_code&, size_t s){ 
     msgBuff.pop_front(); 
     if (!msgBuff.empty())write(); 
    } 
    void handleRead(const boost::system::error_code&, size_t s){ 
     std::istream is(&buf); 
     std::string tmpMsg; 
     std::getline(is, tmpMsg); 

     (server->*serverHandler)(tmpMsg); 
     readMsg(); 
    } 


public: 
    typedef boost::shared_ptr<Connection> pointer; 
    static pointer createInstance(io_service& service){ 
     return pointer(new Connection(service)); 
    } 

    void init(Server* server, void(Server::*serverHandler)(std::string)){ 
     this->server = server; 
     this->serverHandler = serverHandler; 
     writeMsg("hello\n"); 
     readMsg(); 
    } 

    void writeMsg(std::string msg){ 
     strand.dispatch(boost::bind(&Connection::writeStranded, this, msg)); 
    } 

    void readMsg(){ 
     const char delim = '\n'; 
     boost::asio::async_read_until(soc, buf, delim, 
      boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); 

    } 

    tcp::socket& getSocket(){ 
     return soc; 
    } 

}; 

class Server{ 
    tcp::acceptor accept; 
    std::vector<Connection::pointer> connections; 

public: 
    Server(io_service& io_service, int port = 23) :accept(io_service, tcp::endpoint(tcp::v4(), port)){ 
     awaitConnection(); 
    }; 


private: 
    void awaitConnection(){ 
     Connection::pointer con = Connection::createInstance(accept .get_io_service()); 
     accept.async_accept(con->getSocket(), boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error)); 
    } 
    void conAcceptor(Connection::pointer con, const boost::system::error_code& err){ 
     if (err)return; 
     con->init(this, &Server::msgHandler); 
     awaitConnection(); 
     connections.push_back(con); 
    } 
    void msgHandler(std::string str){ 
     for (Connection::pointer ptr : connections){ 
      ptr->writeMsg(str+" by \"name\"\n"); 
     } 
    } 

}; 


int main(){ 
    io_service io_service; 
    Server s(io_service); 
    io_service.run(); 
    system("pause"); 

} 

Upd
Die async_read wurde die Zeichenfolge mit Carriage Return angefügt, die gespeichert wurde, wie es vor dem Delimeter in der Zeichenfolge name hinzugefügt wurde, und jedes Mal, wenn ich versuchte, den Namen erscheinen, alles davor würde durch alle folgenden überschrieben werden. Manchmal würde der Wagenrücklauf wild werden und einige Zeichen vor dem Namen überspringen, was die Suche nach diesem Fehler weiter kompliziert.

+0

Ich empfehle Initialisierung boost :: asio :: streambuf buf'' mit einigen Speicher in dem 'Connection' Konstruktor ...;) – kenba

+0

@ kenba 'streambuf' hat keine Konstruktoren, keine Funktionen, die für das Zuweisen von Speicher zu seinen Objekten definiert sind, also weiß ich nicht wirklich, was Sie sagen. Ich benutze 'basic_streambuf' nicht –

+0

' streambuf'' ist eine Instanz von [basic_streambuf] (http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/basic_streambuf/basic_streambuf.html) . Der 'Connection'Constructor reserviert keinen Speicher für den Empfangspuffer:' buf'. Verstehst du es jetzt? – kenba

Antwort

1

Ich habe es läuft. Ich musste einen Client dafür schreiben ...

Bevor dies in die Produktion geht, sollten Sie sich das Lifetime Handling anschauen. Der normale Weg ist, dass das Verbindungsobjekt ein shared_ptr in seinen gebundenen Handlern an sich selbst hält.

Ich habe C++ 14 lambdas verwenden, da ich finde sie weniger belastend, dass boost :: bind.

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


#include <iostream> 
#include <vector> 
#include <deque> 
#include <iterator> 

typedef boost::asio::io_service io_service; 
typedef boost::asio::ip::tcp tcp; 

class Server; 

class Connection 
    : public boost::enable_shared_from_this<Connection> 
{ 
    io_service::strand strand; 
    tcp::socket  soc; 

    // use double-buffering for the message sending 
    std::deque<std::string> sending, to_send; 

    boost::asio::streambuf buf; 
    Server     *server; 

    void (Server::*serverHandler)(std::string); 

private: 
    Connection(io_service& service) 
     : strand(service) 
     , soc(service) 
    { 

    } 

    void writeStranded(std::string msg) 
    { 
     assert(strand.running_in_this_thread()); // sanity check 
     to_send.push_back(std::move(msg)); 
     maybe_write(); 

    } 

    void maybe_write() 
    { 
     assert(strand.running_in_this_thread()); // sanity check 
     if (sending.empty() and not to_send.empty()) { 
      sending.swap(to_send); 

      // make a buffer sequence 

      auto buffers = std::vector<boost::asio::const_buffers_1>(); 
      buffers.reserve(sending.size()); 
      for (auto& data : sending) { 
       buffers.push_back(boost::asio::buffer(data)); 
      } 
      boost::asio::async_write(soc, buffers, 
            strand.wrap([this](auto&& ec, size_t size) 
               { 
                this->sending.clear(); 
                if (not ec) maybe_write(); 
               })); 
     } 
    } 

    void handleRead(const boost::system::error_code&, size_t s) 
    { 
     std::istream is(&buf); 
     std::string tmpMsg; 
     std::getline(is, tmpMsg); 

     (server->*serverHandler)(tmpMsg); 
     readMsg(); 
    } 


public: 
    typedef boost::shared_ptr<Connection> pointer; 

    static pointer createInstance(io_service& service) 
    { 
     return pointer(new Connection(service)); 
    } 

    void init(Server *server, void(Server::*serverHandler)(std::string)) 
    { 
     this->server  = server; 
     this->serverHandler = serverHandler; 
     writeMsg("hello\n"); 
     readMsg(); 
    } 

    void writeMsg(std::string msg) 
    { 
     strand.dispatch(boost::bind(&Connection::writeStranded, this, msg)); 
    } 

    void readMsg() 
    { 
     const char delim = '\n'; 
     boost::asio::async_read_until(soc, buf, delim, 
             boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred)); 

    } 

    tcp::socket& getSocket() 
    { 
     return soc; 
    } 

}; 

class Server 
{ 
    tcp::acceptor     accept; 
    std::vector<Connection::pointer> connections; 

public: 
    Server(io_service& io_service, int port = 2333) 
     : accept(io_service, tcp::endpoint(tcp::v4(), port)) 
    { 
     awaitConnection(); 
    }; 


private: 
    void awaitConnection() 
    { 
     Connection::pointer con = Connection::createInstance(accept.get_io_service()); 
     accept.async_accept(con->getSocket(), 
          boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error)); 
    } 

    void conAcceptor(Connection::pointer con, const boost::system::error_code& err) 
    { 
     if (err)return; 
     con->init(this, &Server::msgHandler); 
     awaitConnection(); 
     connections.push_back(con); 
    } 

    void msgHandler(std::string str) 
    { 
     for (Connection::pointer ptr : connections) { 
      ptr->writeMsg(str + " by \"name\"\n"); 
     } 
    } 

}; 

struct Client 
{ 
    using protocol = boost::asio::ip::tcp; 

    Client(boost::asio::io_service& exec) 
     : executor_(exec) {} 


    void run(int port) 
    { 

     resolver_.async_resolve(protocol::resolver::query("localhost", std::to_string(port)), 
           strand_.wrap([this](auto&& ec, auto iter) 
              { 
               std::cout << "resolve: " << ec.message() << std::endl; 
               if (not ec) start_connect(iter); 
              })); 

    } 

    void start_connect(protocol::resolver::iterator iter) 
    { 
     boost::asio::async_connect(socket_, iter, 
            strand_.wrap([this](auto&& ec, auto iter) 
               { 
                std::cout << "connect: " << ec.message() << std::endl; 
                if (not ec) { 
                 this->start_reading(); 
                 auto data = std::make_shared<std::string>(
                  "The quick brown fox jumps over the lazy dog\n" 
                   "Farmer bob has a cool tractor\n"); 
                 boost::asio::async_write(socket_, boost::asio::buffer(*data), 
                       strand_ 
                        .wrap([data](auto&& ec, auto size) 
                          { 
                           std::cout << "written: " 
                             << size 
                             << std::endl; 
                          })); 
                } 
               })); 
    } 

    void start_reading() 
    { 
     auto buffer = read_buffer_.prepare(1024); 
     socket_.async_read_some(read_buffer_.prepare(1024), [this](auto&& ec, auto size) 
     { 
      read_buffer_.commit(size); 
      std::istream is(std::addressof(read_buffer_)); 
      std::string s; 
      while(std::getline(is, s)) { 
       std::cout << s << std::endl; 
      } 
      start_reading(); 
     }); 

    } 

    boost::asio::io_service& executor_; 
    boost::asio::io_service::strand strand_{executor_}; 
    protocol::resolver    resolver_{executor_}; 
    protocol::socket    socket_{executor_}; 
    boost::asio::streambuf   read_buffer_; 
}; 

int main() 
{ 
    io_service io_service; 
    Server  s(io_service); 
    Client  c(io_service); 
    c.run(2333); 
    io_service.run(); 
    system("pause"); 

} 

Ausgang (Programm nicht beendet):

resolve: Undefined error: 0 
connect: Undefined error: 0 
written: 74 
hello 
The quick brown fox jumps over the lazy dog by "name" 
Farmer bob has a cool tractor by "name" 
+0

Zusätzliche Hinweise sind sehr geschätzt, danke.Ich werde es morgen überprüfen –

+0

Ich integrierte die Tweaks, die Sie in 'Connection' eingeführt haben und ignorierte die Client-Seite, da ich Telnet nutze, um den Server zu erreichen. Das hat mein Problem nicht gelöst. Die Saiten überlagern @RichardH –

+0

@ Michael.P immer noch interessant. Ich werde heute Abend einen Blick darauf werfen –

Verwandte Themen