2017-07-09 5 views
0

私はチャットを行っています。何らかの理由で、他のクライアント間でユーザーのメッセージを配信する時点で、Server::msgHandlerで囲まれた文字列がasync_writeConnection::writeに破棄され、その文字列の一部のみが実際に読み取られたように見えます。例:boost :: async_writeは文字列の一部をスキップします

構築メッセージ:
"ジャックによってこんにちは人々は" のように表示されます。プリントアウトされていないstring str=Hello peopleです

"ジャックで"。最初は暗黙的な\0の最後であると思っていましたが、それでも意味がありませんでした。メッセージの中で文字列のさまざまな位置を試してみましたが、strの前に他のテキストがある場合、完全にstrを放出するか、予期しない場所に置くかのどちらかを示してください。例えば。

writeMsg("It was said: \n"+str+" by \"name\"\n");
として表示されます:

#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:それは完全な、最小限、コンパイルの例では、 "名前" による
こんにちは人々

を言われた

async_readは、name文字列の区切り文字の前に追加されたように保存されたキャリッジリターンの文字列を追加していました。名前を表示しようとするたびに、その前にあるすべての行が上書きされます。キャリッジリターンがワイルドになり、名前の前にいくつかの文字がスキップされることがあり、このバグの検索がさらに複雑になることがあります。

+0

は、私はブーストを初期化をお勧め:: ASIO ::ストリームバッファ 'Connection'のコンストラクタで、いくつかのメモリを搭載したbuf'' ...;) – kenba

+0

@ kenba 'streambuf'には、オブジェクトにメモリを割り当てるためのコンストラクタも関数も定義されていないので、あなたが何を言っているのか分かりません。 'basic_streambuf'を使用していません –

+0

' streambuf'は[basic_streambuf](http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/basic_streambuf/basic_streambuf.html)のインスタンスです。 。 'Connection'コンストラクタは受信バッファのためにメモリを割り当てません:' buf'。あなたは今理解していますか? – kenba

答えて

1

実行中です。私はそれのためのクライアントを書かなければならなかった...

これが生産に入る前に、生涯の取り扱いを見たいと思うでしょう。通常の方法は、接続オブジェクトがバインドされたハンドラ内でshared_ptrを保持していることです。

ブースト::バインドがそれほど負担が少ないので、私はC++ 14ラムダを使用しています。

#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"); 

} 

出力(プログラムが終了しません):

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

追加の手掛かりがありがとうございます、ありがとうございます。私は明日それを見直すでしょう –

+0

私はあなたがサーバーに到達するためにTelnetを使用するように、クライアント側を無視して、あなたが導入した調整を、 'Connection'に取り入れました。これは私の問題を解決しませんでした。文字列はまだ@RichardH –

+0

@ Michael.P興味深いオーバーレイです。私は今夜​​見てみましょう –

関連する問題