【问题标题】:boost asio async_read: the read message adds to itselfboost asio async_read:读取的消息添加到自身
【发布时间】:2014-10-06 00:29:24
【问题描述】:

我将我的 PC 用作服务器。客户端发送如下消息:“PART1:Part2”,服务器执行必要的操作。我使用 boost 的 asio 作为服务器代码。

    void start_read()
    {
        boost::asio::async_read(socket_, input_buffer_,
            boost::asio::transfer_at_least(1),
            boost::bind(&tcp_connection::handle_read, shared_from_this(),
            boost::asio::placeholders::error));
    }

    // When stream is received handle the message from the client
    void handle_read(const boost::system::error_code& error)
    {
        if (!error)
        {
            boost::asio::streambuf::const_buffers_type bufs = input_buffer_.data();
            std::string msgstr(boost::asio::buffers_begin(bufs),
                               boost::asio::buffers_begin(bufs) +
                               input_buffer_.size());

            std::vector<std::string> msgVector;
            boost::split(msgVector, msgstr, boost::is_any_of(":"));

            messageFromClient_ = msgVector[0];
            valueFromClient_ = msgVector[1];
};

每秒向服务器发送消息,生成的 msgstr 如下所示:

PART1:part2a
PART1:part2bPART1:part2b
PART1:part2cPART1:part2cPART1:part2c
PART1:part2dPART1:part2dPART1:part2dPART1:part2d

这不是我想要的。我不想包含上一个缓冲区中的数据,即我想要这个:

PART1:part2a
PART1:part2b
PART1:part2c
PART1:part2d

我知道问题很可能出在此处:

            std::string msgstr(boost::asio::buffers_begin(bufs),
                               boost::asio::buffers_begin(bufs) +
                               input_buffer_.size());

但是,我找不到适用于我的情况的正确代码。

编辑: 尝试这样做:

std::istream response_istream(&input_buffer_);
std::string msgstr;
response_istream >> msgstr;

前 3 次,我得到了我需要的信息,但随后消息成倍增加。总是这样:

PART1:part2a
PART1:part2b
PART1:part2c
PART1:part2dPART1:part2d
PART1:part2ePART1:part2e
PART1:part2fPART1:part2fPART1:part2fPART1:part2f
PART1:part2gPART1:part2g

提前非常感谢。

【问题讨论】:

    标签: c++ asynchronous buffer boost-asio tcpserver


    【解决方案1】:

    streambuf::data() 成员函数返回代表输入序列的缓冲区。为了避免再次访问数据,可以使用streambuf::consume() 成员函数从输入序列的开头删除字符。在这种情况下,一旦将数据从input_buffer_ 复制到msgstr,就可以使用以下命令清除输入序列:

    input_buffer_.consume(input_buffer_.size());
    

    这是一个完整的最小示例demonstratingconsume() 的行为:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    #include <boost/lexical_cast.hpp>
    
    // This example is not interested in the handlers, so provide a noop function
    // that will be passed to bind to meet the handler concept requirements.
    void noop() {}
    
    int main()
    {
      using boost::asio::ip::tcp;
      boost::asio::io_service io_service;
    
      // Create all I/O objects.
      tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
      tcp::socket server_socket(io_service);
      tcp::socket client_socket(io_service);
    
      // Connect client and server sockets.
      acceptor.async_accept(server_socket, boost::bind(&noop));
      client_socket.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
      io_service.run();
    
      // No-consuming case.
      { 
        std::cout << "Non-consuming example" << std::endl;
        boost::asio::streambuf streambuf;
        for (int i = 0; i < 5; ++i)
        {
          std::string data = "test";
          data += boost::lexical_cast<std::string>(i);
    
          // Write to server.
          std::size_t bytes_transferred =
              write(client_socket, boost::asio::buffer(data));
    
          // Read from client.
          read(server_socket, streambuf, 
               boost::asio::transfer_exactly(bytes_transferred));
    
          // Print results.
          std::string read_data(
            boost::asio::buffers_begin(streambuf.data()),
            boost::asio::buffers_end(streambuf.data()));
          std::cout << "size: " << streambuf.size() << ", "
                    << "read: " << read_data << std::endl;
        }
      }
    
      // Consuming case.
      { 
        std::cout << "Consuming example" << std::endl;
        boost::asio::streambuf streambuf;
        for (int i = 0; i < 5; ++i)
        {
          std::string data = "test";
          data += boost::lexical_cast<std::string>(i);
    
          // Write to server.
          std::size_t bytes_transferred =
              write(client_socket, boost::asio::buffer(data));
    
          // Read from client.
          read(server_socket, streambuf, 
               boost::asio::transfer_exactly(bytes_transferred));
    
          // Print results.
          std::string read_data(
            boost::asio::buffers_begin(streambuf.data()),
            boost::asio::buffers_end(streambuf.data()));
          std::cout << "size: " << streambuf.size() << ", "
                    << "read: " << read_data << std::endl;
    
          streambuf.consume(bytes_transferred);
        }
      }
    }
    

    还有输出:

    Non-consuming example
    size: 5, read: test0
    size: 10, read: test0test1
    size: 15, read: test0test1test2
    size: 20, read: test0test1test2test3
    size: 25, read: test0test1test2test3test4
    Consuming example
    size: 5, read: test0
    size: 5, read: test1
    size: 5, read: test2
    size: 5, read: test3
    size: 5, read: test4
    

    注意在消费情况下,之前的数据是如何被丢弃的。

    【讨论】:

    • 非常感谢,坦纳。我现在有这个:` boost::asio::streambuf::const_buffers_type bufs = input_buffer_.data();` std::string msgstr(boost::asio::buffers_begin(bufs),boost::asio::buffers_begin(bufs) +input_buffer_.size()); input_buffer_.consume(input_buffer_.size()); 这使它成为一个问题,正如我在编辑后的文本中所显示的那样,即前三次,字符串是正常的,但后来是这样的:PART1:part2aPart1:part2a。我时不时地在字符串中包含三个组件。嗯..谢谢!
    • 编辑:坦纳,再次感谢您的帮助。这是非常有用的信息。我想我知道为什么会这样。客户端每秒发送一条消息,服务器应该在消息发送后立即进行一些操作。但是服务器进行操作需要时间,有时是 2 秒,有时是 3 秒。因此,缓冲区会随着客户端发送的消息而累积。最好的问候,
    • @IgorTupitsyn TCP 是一个流,因此没有定义的消息边界。因此,代码必须能够处理累积值和部分值。例如,发送者可以发送“PART1:part2a”,但对handle_read() 的调用只读取“PAR”。这个answer 提供了一些关于如何处理固定大小或可变长度应用程序协议的参考。此外,由于负载大小,禁用 Nagle(如 here 所示)可能会提高性能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 2013-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多