Boost Asio async_read doesn't stop reading? - c++

So,
I've been playing around with the Boost asio functions and sockets (specifically the async read/write). Now, I thought that boost::asio::async_read only called the handler when a new buffer came in from the network connection... however it doesn't stop reading the same buffer and thus keeps calling the handler. I've been able to mitigate it by checking the number of bytes transferred, however it is basically in a busy-waiting loop wasting CPU cycles.
Here is what I have:
class tcp_connection : : public boost::enable_shared_from_this<tcp_connection>
{
public:
// other functions here
void start()
{
boost::asio::async_read(socket_, boost::asio::buffer(buf, TERRAINPACKETSIZE),
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
const unsigned int TERRAINPACKETSIZE = 128;
char buf[TERRAINPACKETSIZE];
void handle_read(const boost::system::error_code& error, size_t bytesT)
{
if (bytesT > 0)
{
// Do the packet handling stuff here
}
boost::asio::async_read(socket_, boost::asio::buffer(buf, TERRAINPACKETSIZE),
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
};
Some stuff is cut out, but basically a new connection gets created then start() is called. Is there something I'm missing so that the handle_read method doesn't get continuously called?

A wild guess: Do you check error in handle_read? If the socket is in an error state for some reason, I guess that the nested call to async_read made from handle_read will immediately "complete", resulting in an immediate call to handle_read

I was having the same problem. In my case, I was reading into a std::vector<unsigned_char> like this:
boost::asio::async_read(socket_,
st::asio::buffer(*message,message->size()),
boost::bind(
&ActiveSocketServerSession::handleFixLengthRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
I have an adaptative vector for accept a variant number of bytes
std::vector<unsigned char>* message;
message = new std::vector<unsigned char> (sizePacket);
When I was receiving the first packet all was going fine, but after the first, never stops unlocking handle_reader with no data.
My solution was to delete my vector and alloc space again after processing it:
void ActiveSocketServerSession::handleFixLengthRead( const boost::system::error_code& error,
std::size_t bytes_transferred){
--> processing your data (save to another site)
--> delete message;
--> message = new std::vector<unsigned char> (sizePacket);
//starting to read again
boost::asio::async_read(socket_,
boost::asio::buffer(*message,message->size()),
boost::bind(
&ActiveSocketServerSession::handleFixLengthRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
}else{
logMessage.str("");
logMessage << "Error handling data id: "<<getId()<< "from port";
}
}
After putting these two lines all goes fine.

Related

Boost Asio: Strange when use streambuf and async_write in mutl-thread

My program has a buffer when sending data. Instead of directly calling async_write every time for send small packets, try to make the send method return quickly, use streambuf as the sending buffer, and try to send large packets.
The problem encountered now is that when multiple threads call send at the same time, there is a small probability that the opposite end may receive duplicate data packets or messy data. Here is my code:
void ClientConnection::send(const string* buffer, function<void (bool status)> callback) {
{
unique_lock<mutex> lck(*_ioLockPtr);
ostream os(_sendBufferPtr.get());
os << *buffer;
}
delete buffer;
callback(true);
_sendBuffer();
}
void ClientConnection::_sendBuffer() {
unique_lock<mutex> lck(*_ioLockPtr);
size_t bufferSize = _sendBufferPtr->size();
if (!bufferSize || _sendingBufferCount > 0) {
return;
}
++_sendingBufferCount;
async_write(*_socketPtr, _sendBufferPtr->data(), boost::asio::transfer_exactly(bufferSize), boost::bind(&ClientConnection::_handleWrite,
shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
_sendBufferPtr->consume(bufferSize);
}
void ClientConnection::_handleWrite(const boost::system::error_code& error, size_t bytes_transferred) {
if (!error) {
unique_lock<mutex> lck(*_ioLockPtr);
size_t bufferSize = _sendBufferPtr->size();
if (bufferSize) {
async_write(*_socketPtr, _sendBufferPtr->data(), boost::asio::transfer_exactly(bufferSize), boost::bind(&ClientConnection::_handleWrite,
shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
_sendBufferPtr->consume(bufferSize);
} else {
--_sendingBufferCount;
}
} else {
{
unique_lock<mutex> lck(*_ioLockPtr);
--_sendingBufferCount;
}
_close();
}
}
The relevant variables are defined as follows:
shared_ptr<boost::asio::streambuf> _sendBufferPtr;
uint8_t _sendingBufferCount;
Please help me to understand how to solve this problem, thanks!
The problem encountered now is that when multiple threads call send at the same time
This is strictly prohibited as per the documentation:
This operation is implemented in terms of zero or more calls to the stream's async_write_some function, and is known as a composed operation. The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.
To serialize the async operations, you may want to use a strand.

Operation canceled boost asio async_receive_from

I have an UDP Server set up with boost/asio (I copied the example and just changed a few things). Below is the code:
udp_server.hpp
using boost::asio::ip::udp;
class udp_server {
public:
udp_server(boost::asio::io_service&, int);
private:
boost::array<char, 256> recBuffer;
udp::socket socket_;
udp::endpoint remote_endpoint_;
void start_receive();
void handle_receive(const boost::system::error_code&, std::size_t);
void handle_send(boost::shared_ptr<std::string> /*message*/,
const boost::system::error_code& /*error*/,
std::size_t /*bytes_transferred*/)
{}
};
and udp_server.cpp
udp_server::udp_server( boost::asio::io_service& io_service,
int port)
: socket_(io_service, udp::endpoint(udp::v4(), port)) {
serverNotifications.push_back("UDP Server class initialized.");
start_receive();
}
void udp_server::start_receive() {
socket_.async_receive_from(
boost::asio::buffer(recBuffer),
remote_endpoint_,
boost::bind(&udp_server::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
serverNotifications.push_back("Starting to receive UDP Messages.");
}
void udp_server::handle_receive(const boost::system::error_code& error,
std::size_t size) {
serverNotifications.push_back("RecFrom: " + remote_endpoint_.address().to_string());
if (!error) {
// I do data stuff here
} else {
errors.push_back("Handle Receive error: " + error.message());
}
}
After initializing the Server with:
try {
udp_server server(io_service, ApplData.PORT, (size_t)ApplData.BUFLEN);
} catch (std::exception& e) {
// error handling
}
and running it with io_service.run() in a seperate try catch in another function I get some problems:
My Callback function handle_receive gets called without any UDP message getting send in the whole network (aka only my laptop without connection)
error.message() returns "Operation canceled"
remote_endpoint_.address().to_string() returns "acfc:4000:0:0:7800::%2885986016" which I can't identify as something useful
Also I recognized that my io_service is stopping all the time, but in my understanding it should run all the time, right?
I already thought about referencing this in the callback function bind with a shared_from_this ptr, but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
Can someone explain thy this failure occurs, what these errors tell me about my code or what I can do to avoid them?
Nevermind, Rubberduck debugging was enough. I just read the line
but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
and noticed, that I actually didn't have this and this was the error.

C++ wait for all async operation end

I have started N same async operations(e.g. N requests to database), so i need to do something after all this operations end. How i can do this? (After one async operation end, my callback will be called).
I use C++14
Example
i use boost.asio to write some data to socket.
for (int i = 0; i < N; ++i)
{
boost::asio::async_write(
m_socket,
boost::asio::buffer(ptr[i], len[i]),
[this, callback](const boost::system::error_code& ec, std::size_t )
{
callback(ec);
});
}
So i need to know when all my writes ends;
first of all, never call async_write in a loop. Each socket may have only one async_write and one async_read outstanding at any one time.
boost already has provision for scatter/gather io.
This snippet should give you enough information to go on.
Notice that async_write can take a vector of vectors as a 'buffer' and it will fire the handler exactly once, once all the buffers have been written.
struct myclass {
boost::asio::ip::tcp::socket m_socket;
std::vector<std::vector<char>> pending_buffers;
std::vector<std::vector<char>> writing_buffers;
void write_all()
{
assert(writing_buffers.size() == 0);
writing_buffers = std::move(pending_buffers);
boost::asio::async_write(
m_socket,
boost::asio::buffer(writing_buffers),
std::bind(&myclass::write_all_handler,
this,
std::placeholders::_1,
std::placeholders::_2));
}
void write_all_handler(const boost::system::error_code& ec, size_t bytes_written)
{
writing_buffers.clear();
// send next load of data
if (pending_buffers.size())
write_all();
// call your callback here
}
};

what is the easiest way to extend an existing data structure to include semaphore or similar methods in c++?

I have an existing c++ code.
boost::asio::ip::address m_sender_IP_address;
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
boost::bind(&Udp_comm::handle_receive, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Udp_comm::handle_receive(const boost::system::error_code& error, const std::size_t bytes_transferred)
{
std::string recvd_message(m_recv_buffer.begin(), m_recv_buffer.begin() + bytes_transferred);
m_sender_IP_address = m_sender_endpoint.address();//////////wait here
//continue to listening to future messages
start_receive();
process_message(m_sender_IP_addres.to_string(), recvd_message);
}
How to protect m_sender_IP_address from getting overwritten by next UDP message received? I want lock access to that variable from 2nd line in handle_receive method to the 1st line in process_message method
can I also acheive something similar without using locks?
A semaphore is not necessary here. You can use a strand to ensure an asynchronous handler is invoked concurrently once at most. To do this, create a wrapped handler for the async_receieve_from() callback
void Udp_comm::start_receive()
{
//receive UDP message
m_sock_r.async_receive_from(
boost::asio::buffer(m_recv_buffer),
m_sender_endpoint,
m_strand.wrap(
boost::bind(
&Udp_comm::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
)
);
}
Here m_strand is a member of Udp_comm. Doing this will guarantee only one thread can mutate m_sender_IP_address.

Am I getting a race condition with my boost asio async_read?

bool Connection::Receive(){
std::vector<uint8_t> buf(1000);
boost::asio::async_read(socket_,boost::asio::buffer(buf,1000),
boost::bind(&Connection::handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
int rcvlen=buf.size();
ByteBuffer b((std::shared_ptr<uint8_t>)buf.data(),rcvlen);
if(rcvlen <= 0){
buf.clear();
return false;
}
OnReceived(b);
buf.clear();
return true;
}
The method works fine but only when I make a breakpoint inside it. Is there an issue with timing as it waits to receive? Without the breakpoint, nothing is received.
You are trying to read from the receive buffer immediately after starting the asynchronous operation, without waiting for it to complete, that is why it works when you set a breakpoint.
The code after your async_read belongs into Connection::handler, since that is the callback you told async_read to invoke after receiving some data.
What you usually want is a start_read and a handle_read_some function:
void connection::start_read()
{
socket_->async_read_some(boost::asio::buffer(read_buffer_),
boost::bind(&connection::handle_read_some, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void connection::handle_read_some(const boost::system::error_code& error, size_t bytes_transferred)
{
if (!error)
{
// Use the data here!
start_read();
}
}
Note the shared_from_this, it's important if you want the lifetime of your connection to be automatically taken care of by the number of outstanding I/O requests. Make sure to derive your class from boost::enable_shared_from_this<connection> and to only create it with make_shared<connection>.
To enforce this, your constructor should be private and you can add a friend declaration (C++0x version; if your compiler does not support this, you will have to insert the correct number of arguments yourself):
template<typename T, typename... Arg> friend boost::shared_ptr<T> boost::make_shared(const Arg&...);
Also make sure your receive buffer is still alive by the time the callback is invoked, preferably by using a statically sized buffer member variable of your connection class.