boost::asio::async_read_until reads all data instead of just some

家住魔仙堡 提交于 2019-12-01 14:07:06

问题


I'm modify the Boost Asio echo example to use async_read_until to read the input word by word. Even though I am using async_read_until, all the data sent seems to be read from the socket. Could someone please advise:

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

using boost::asio::ip::tcp;

class session
{
public:
  session(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
 std::cout<<"starting"<<std::endl;
  boost::asio::async_read_until(socket_, buffer, ' ',
        boost::bind(&session::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {

 std::ostringstream ss;
 ss<<&buffer;
 std::string s = ss.str();
 std::cout<<s<<std::endl;

 if (!error)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(s),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
 std::cout<<"handling write"<<std::endl;
    if (!error)
    {
    }
    else
    {
      delete this;
    }
  }

private:
  tcp::socket socket_;
  boost::asio::streambuf buffer;
};

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

  void handle_accept(session* new_session,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_session->start();
      new_session = new session(io_service_);
      acceptor_.async_accept(new_session->socket(),
          boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }
    else
    {
      delete new_session;
    }
  }

private:
  boost::asio::io_service& io_service_;
  tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server s(io_service, atoi(argv[1]));

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

回答1:


Read the description of async_read_until carefully. It says:

After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter.

What this means in your case is that inside handle_read(), you should only access the first bytes_transferred bytes from the buffer. As of now, your bytes_transferred parameter is unused.




回答2:


The answer by Cubbi is correct: async_read_until() might read extra data into the buffer.

The other answer by ecoretchi is slightly dangerous. It is not incorrect, but only works work this specific question, which reads input word by word (until ' ').

If you are reading input line by line (until '\n'), you will lose data with that solution. This happens because if you use commit(), and >> operator splits your data at something else than newline (for example, you have numbers and whitespaces in the data) all subsequent data is lost.

When you are reading line by line, you should to use getline() to get one line from the buffer. This leaves rest of the data waiting in the streambuf class and allows you to read it with subsequent calls. This is also the reason you must have the streambuf as a member variable, not as a local variable in a function; if it was a local variable, any extra data beyond the first line would be lost.

std::istream is(&buffer);
std::string result_line;
std::getline(is, result_line);



回答3:


This is by design. Asio does not have good place to store excess data, so it handles them to you. Asio is not parser library, take a look at boost::spirit, or do parsing ad hoc, like our ancestors. :)




回答4:


Try this:

void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {

    buffer.commit(bytes_transferred);
    std::istream istrm(&buffer);
    std::string str; 
    istrm>>str;
    ...


来源:https://stackoverflow.com/questions/3058589/boostasioasync-read-until-reads-all-data-instead-of-just-some

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!