8

I'm trying to modify the echo server example from boost asio and I'm running into problem when I try to use boost::asio::async_read_until. Here's the code:

    #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_, boost::asio::buffer(data_, max_length), ' ',
        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::cout<<"handling read"<<std::endl;
    if (!error)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
    /*
      socket_.async_read_some(boost::asio::buffer(data_, max_length),
          boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
   */ }
    else
    {
      delete this;
    }
  }

private:
  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

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;
}

The problem is when I try to compile I get this weird error:

server.cpp: In member function ‘void session::start()’: server.cpp:27: error: no matching function for call to ‘async_read_until(boost::asio::basic_stream_socket >&, boost::asio::mutable_buffers_1, char, boost::_bi::bind_t, boost::_bi::list3, boost::arg<1> ()(), boost::arg<2> ()()> >)’

Can someone please explain what's going on? From what I can tell the arguments to async_read_until are correct.

Thanks!

3 Answers 3

9

The second argument of async_read_until should be a streambuf object into which the data will be read. To put it simple, you need to pass a boost::asio::streambuf by reference, not a boost::asio::buffer by value.

Sign up to request clarification or add additional context in comments.

1 Comment

Absolute correct analysis from what I can tell without compiling
5

There is no need to use streambufs. There are overloads that accept dynamic buffers. Dynamic is key since read_until implies the need for a buffer that can increase its size at the callee's discretion. Therefore, all you need is to replace the call to boost::asio::buffer with boost::asio::dynamic_buffer and call it on either std::string or std::vector<char>.

...

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

...

 private:
  tcp::socket socket_;
  std::string data_;
  // or
  //std::vector<char> data_;
};

I would also get rid of boost::bind since it's an overkill and use a lambda (use them as much as you can)

[this](const boost::system::error_code& error, size_t bytes_transferred) {
  handle_read(error, bytes_transferred);
}

2 Comments

Finally I found my answer! Boost documentation showcases examples using raw std::string that DON'T compile, and this made the trick.
The problem with using std::string or std::vector<char> instead of asio::streambuf is that they do not support efficient erasure from the start of the sequence (which is what you'll no doubt be doing in your handle_read() function). This means that under certain conditions they could end up doing a quadratic amount of copying.
1

const (in)correctness may also cause this type of error message.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.