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