2017-07-09 90 views
0

我正在聊天。出於某種原因,在其他客戶端之間分發用戶消息時,由Server::msgHandler組成的字符串在Connection::write中被async_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

我建議初始化的boost ::支持ASIO ::流緩衝buf''與在'Connection'構造......一些內存;) – 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'constructor不爲接收緩衝區分配任何內存:'buf'。你現在明白了麼? – kenba

回答

1

我知道它運行。我必須爲它編寫一個客戶端...

在開始生產之前,您需要查看生命週期處理。正常的方式是連接對象在其綁定的處理程序中擁有一個shared_ptr給自己。

我已經使用C++ 14 lambda表達式,因爲我發現它們不那麼繁重,即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"); 

} 

輸出(程序不終止):

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訪問服務器。這並沒有解決我的問題。字符串仍然覆蓋@RichardH –

+0

@ Michael.P有趣。我今晚會看看 –