boost async_read_until combine match_condition wit

2019-08-09 08:12发布

问题:

Is there way to combain two conditions read until a matching character is found or 128 bytes received?

It is possible to limit streambuf size:

inBuf = std::make_shared< boost::asio::streambuf>(limit_size);

But when handler with reason "Element not found" there is no available data in stream and transferred bytes also is 0.

The idea is to read until ';' or 128 bytes received

My code is following:

class match_char
{
public:
  explicit match_char(char c, int len) : c_(c), len_(len) {}

  template <typename Iterator>
  std::pair<Iterator, bool> operator()(
      Iterator begin, Iterator end) const
  {
    Iterator i = begin;

    const size_t buffer_len = std::distance(begin,end); <-- this is not work usualy distance here is 1

    if(buffer_len >= len_)
    {
      std::advance(i, len_);
      return std::make_pair(i, true);
    }

    while (i != end)
      if (c_ == *i++) {
        return std::make_pair(i, true);
      }
    return std::make_pair(i, false);
  }

private:
  char c_;
  int len_;
};

namespace boost {
    namespace asio {
      template <> struct is_match_condition<match_char>
      : public boost::true_type {};
    }
}


boost::asio::streambuf b;

void main() {

boost::asio::async_read_until(port, b,
           match_char(';', 128) , &handler);

           }

回答1:

A custom MatchCondition condition will not allow one to limit the number of bytes read from the stream to not exceed n number of bytes. The read_until() family of functions all allow for data beyond the delimiter to be read from the Stream and into the streambuf. In essence, read_until() will prepare a buffer from the streambuf, read a chunk of data from Stream into the prepared buffer, then use the MatchCondition to check if the completion conditions have been met.

In order to read no more than n bytes and complete early if a desired delimiter is found, consider constructing the boost::asio::streambuf with a max size, and then using the read_until() operations. For example, the following will read no more than 128 bytes from the stream and complete early if the ';' delimiter is found:

boost::asio::streambuf buffer(128);
boost::system::error_code error;
std::size_t bytes_transferred = boost::asio::read_until(
    stream, buffer, ';', error);

In the above example

  • If 128 bytes were read and the delimiter was not found, then error will be boost::asio::error::not_found and buffer.size() will be 128.
  • If the delimiter was found, then error will be successful and bytes_transferred will be the number of bytes up to and including the delimiter that reside within the buffer's input sequence. Note that buffer.size() may be larger than bytes_transferred, but will not exceed 128.
  • If 128 bytes were not read and a delimiter was not found, then error will contain the appropriate error condition as to why the read operation failed, such as boost::asio::error::eof if the remote peer closed the connection.

Here is a complete example demonstrating the behavior of streambuf:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.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() {}

std::string make_string(boost::asio::streambuf& streambuf, std::size_t n)
{
 return {buffers_begin(streambuf.data()),
         buffers_begin(streambuf.data()) + n};
}

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();

  // Write data to server that contains a delimiter.
  auto bytes_written = boost::asio::write(server_socket,
      boost::asio::buffer(std::string("12345;67890")));

  // Wait for all data to be received.
  while (bytes_written != client_socket.available())
  {
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  // Create a streambuf too small to read up to the delimter.
  {
    const std::size_t MAX_SIZE = 2;
    boost::asio::streambuf read_buffer(MAX_SIZE);

    boost::system::error_code error;
    auto bytes_read = boost::asio::read_until(
        client_socket, read_buffer, ';', error);

    // Expect an error as the delim was not found, so the read_until
    // return value is 0.  However, data was read into the buffer.
    assert(boost::asio::error::not_found == error);
    assert(bytes_read == 0);
    assert(read_buffer.size() == 2); // "12" out of "12345;67890";

    // Verify additional data was not read from the stream.
    assert((bytes_written - MAX_SIZE) == client_socket.available());
  }

  // Default construct a streambuf, which has a large max.
  {
    boost::asio::streambuf read_buffer;

    // Read from the socket.
    boost::system::error_code error;
    auto bytes_read = boost::asio::read_until(
        client_socket, read_buffer, ';', error);

    // Verify that at least "345;" was read.
    assert(!error);
    assert(bytes_read != 0);
    assert(make_string(read_buffer, bytes_read) == "345;");

    // Not a guarantee, but the implementation may (and likely will)
    // read beyond the delimiter.  If so, the streambuf's size will
    // be larger than return value from read_until, as it returns the
    // number of bytes up to and including the delimiter.
    if (bytes_read < read_buffer.size())
    {
      std::cout << "Read beyond delimiter" << std::endl;
    }
  }

  std::cout << "bytes remaining: " << client_socket.available() <<
               std::endl;
}

Output:

Read beyond delimiter
bytes remaining: 0