Reputation: 373
I am currently experiencing troubles using boost::asio, I want to write and read like in the following piece of code. Write works fine, but reading always returns zero. Actually I also found out that every site refers to the
read_some
function instead of the
readsome
function I have on my system in the library ...
Anybody who can give me a hint on this?
boost::asio::ip::tcp::iostream tcp_stream;
tcp_stream.connect("localhost", "12345");
while(1)
{
char inp[6]={0,0,0,0,0,0};
tcp_stream<<"Test"<<std::endl;//Works fine get it with netcat (nc -l 12345)
size_t r = tcp_stream.readsome(inp,5);//Always 0
//std::string a;
//tcp_stream>>a; //Works but blocks and gives me all bytes it has.
//std::cout<<a<<std::endl;
//std::cout<<"RDBUF: "<<tcp_stream.rdbuf();
//rdbuf blocks here, never resuming and outputting
//everything I write with netcat to command line...
if(r>0)
{
std::cout<<inp<<std::endl;
}
else //<<< always goes here
{
std::cout<<"received nothing!"<<std::endl;
}
sleep(1);
}
What I would like to have is that I read 0-5 bytes from the current interface. (non-blocking)
Upvotes: 0
Views: 1207
Reputation: 51931
The issue is that std::basic_istream<>::read_some()
will read from the associated streambuf
's input sequence, and the asio::basic_socket_streambuf
's input sequence includes data that has been read from the socket, but not yet consumed from the streambuf. It does not include data that is available to be read from the socket without blocking.[1]
To work around this behavior, one could determine the amount of data available to be read without blocking from both the streambuf and its underlying socket, then issue a std::basic_istream<>read()
operation:
/// @brief Read up to `n` characters from `stream` and store them
/// into `buffer` without blocking. This may not read all
/// of the requested amount of bytes.
template <typename... Args>
std::streamsize readsome(
boost::asio::basic_socket_iostream<Args...>& stream,
char* buffer,
std::streamsize n)
{
std::streamsize available =
stream.rdbuf()->in_avail() // available on input sequence
+ stream.rdbuf()->available(); // available on socket
n = std::min(n, available);
if (n == 0) return 0;
stream.read(buffer, n);
return n;
}
Here is a complete example demonstrating this functionality:
#include <chrono> // std::chrono::seconds
#include <iostream> // std::cout, std::endl
#include <string> // std::to_string
#include <thread> // std::thread
#include <boost/asio.hpp>
/// @brief Read up to `n` characters from `stream` and store them
/// into `buffer` without blocking. This may not read all
/// of the requested amount of bytes.
template <typename... Args>
std::streamsize readsome(
boost::asio::basic_socket_iostream<Args...>& stream,
char* buffer,
std::streamsize n)
{
std::streamsize available =
stream.rdbuf()->in_avail() // available on input sequence
+ stream.rdbuf()->available(); // available on socket
n = std::min(n, available);
if (n == 0) return 0;
stream.read(buffer, n);
return n;
}
int main()
{
using boost::asio::ip::tcp;
const std::array<char, 6> expected_data = {100, 101, 102, 103, 104, 105};
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket(io_service);
// Run stream in its own thread.
std::thread client_thread(
[&]
{
// Connect the stream to the acceptor.
auto endpoint = acceptor.local_endpoint();
tcp::iostream stream(endpoint.address().to_string(),
std::to_string(endpoint.port()));
// Block until 6 bytes are available.
while (stream.rdbuf()->available() < 6)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Read 1 byte out of the 6 avaialble.
std::array<char, 6> actual_data{};
auto bytes_transferred = readsome(stream, begin(actual_data), 1);
assert(bytes_transferred == 1);
assert(std::equal(
begin(actual_data),
begin(actual_data) + bytes_transferred,
begin(expected_data)));
// Attempt to read 6 bytes, although only 5 are available.
bytes_transferred = readsome(stream, begin(actual_data),
sizeof actual_data);
assert(bytes_transferred == 5);
assert(std::equal(
begin(actual_data),
begin(actual_data) + bytes_transferred,
begin(expected_data) + 1));
// Attempt to read 6 more bytes, even though 0 bytes are available.
bytes_transferred = readsome(stream, begin(actual_data),
sizeof actual_data);
assert(bytes_transferred == 0);
});
// Connect the sockets then write to the stream.
acceptor.accept(socket);
boost::asio::write(socket, boost::asio::buffer(expected_data));
// Wait for the stream to complete.
client_thread.join();
}
1. The tcp::stream
documentation and its related classes are not well documented. I am not versed enough to know if this behavior is intentional, a bug, or has merely been overlooked. The documentation still remains fairly terse in the current networking-ts draft.
Upvotes: 2