boost::asio::async_read texutal stop condition? - c++

I'm writing a server with Boost, something pretty simple - accept an XML message, process, reply. But I'm running into trouble at telling it when to stop reading.
This is what I have right now: (_index is the buffer into which the data is read)
std::size_t tcp_connection::completion_condition(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
int ret = -1;
std::istream is(&_index);
std::string s;
is >> s;
if (s.find("</end_tag>") != std::string.npos) ret = 0;
return ret;
}
void tcp_connection::start()
{
// Get index from server
boost::asio::async_read(_socket, _index, &(tcp_connection::completion_condition),
boost::bind(&tcp_connection::handle_read, shared_from_this(), boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
This doesn't compile, since I have to define completion_condition as static to pass it to async_read; and I can't define _index as static since (obviously) I need it to be specific to the class.
Is there some other way to give parameters to completion_condition? How do I get it to recognize the ending tag and call the reading handler?

You can pass pointers to member functions. The syntax for doing it with C++ is tricky, but boost::bind hides it and makes it fairly easy to do.
An example would be making completion_condition non-static and passing it to async_read as such:boost::bind(&tcp_connection::completion_condition, this, _1, _2)
&tcp_connection::completion_condition is a pointer to the function. this is the object of type tcp_connection to call the function on. _1 and _2 are placeholders; they will be replaced with the two parameters the function is called with.

Related

boost::asio problem passing dynamically sized data to async handler

I am processing custom tcp data packet with boost. Since all operations are asynchronously a handler must be called to process the data. The main problem is that I don't know how to pass the data to the handler when the size is not known at compiletime?
For example, say you receive the header bytes, parse them which tells you the length of the body:
int length = header.body_size();
I somehow need to allocate an array with the size of the body and then call the handler (which is a class member, not a static function) and pass the data to it. How do I do that properly?
I tried different things such as but always ended up getting a segfault or I had to provide a fixed size for the body buffer which is not what I want. An attempt I made can be found below.
After receiving the header information:
char data[header.body_size()];
boost::asio::async_read(_socket, boost::asio::buffer(data, header.body_size()),
boost::bind(&TCPClient::handle_read_body, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred, data));
The handler:
void TCPClient::handle_read_body(const boost::system::error_code &error, std::size_t bytes_transferred,
const char *buffer) {
Logger::log_info("Reading body. Body size: " + std::to_string(bytes_transferred));
}
This example throws a segfault.
How can I allocate a buffer for the body after knowing the size?
And how can I then call the handler and passing over the error_code, the bytes_transferred and the body data?
An example snippet would be really appreciated since the boost-chat examples that do this are not very clear to me.
char data[header.body_size()]; is not standard in C++ and will become invalid once it goes out of scope while async_read requires buffer to remain alive until completion callback is invoked. So you should probably add a field to TCPClient holding a list of data buffers (probably of std::vector kind) pending to be received.
All you need to do is to create buffer onto heap instead of stack. In place of VLA - char [sizeAtRuntime] you can use std::string or std::vector with std::shared_ptr. By using string/vector you can set buffer to have any size and by using shared_ptr you can prolong lifetime of your buffer.
Version with bind:
void foo()
{
std::shared_ptr<std::vector<char>> buf = std::make_shared<std::vector<char>>(); // buf is local
buf->resize( header.body_size() );
// ditto with std::string
boost::asio::async_read(_socket, boost::asio::buffer(*buf),
boost::bind(&TCPClient::handle_read_body,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
buf)); // buf is passed by value
}
void handle_read_body(const boost::system::error_code&,
size_t,
std::shared_ptr<std::vector<char>>)
{
}
in above example buf is created onto stack and points to vector onto heap, because bind takes its arguments by value, so buf is copied and reference counter is increased - it means your buffer still exists when async_read ends and foo ends.
You can achive the same behaviour with lambda, then buf should be captured by value:
void foo()
{
std::shared_ptr<std::vector<char>> buf = std::make_shared<std::vector<char>>(); // buf is local
buf->resize( header.body_size() );
// ditto with std::string
boost::asio::async_read(_socket, boost::asio::buffer(*buf),
boost::bind(&TCPClient::handle_read_body, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred, buf)); // buf is passed by value
boost::asio::async_read(_socket, boost::asio::buffer(*buf),
[buf](const boost::system::error_code& , size_t)
^^^ capture buf by value, increates reference counter of shared_ptr
{
});
}

boost::bind how to define a function that takes a callback as argument

I am trying to write a library module that invokes boost::asio operations. This module would be used by an application logic layer for further processing, based on the results.
My requirement is plain and simple. What all I need is to be able to specify a callback fn in my Util class below, that can take any function ptr, or boost::bind(member fn), or whatever, and invoke that callback, once the async code is complete.
class Util {
public:
void sendMsg(const Msg& m, <some callback fn here...>) {
// Make a call to boost::asio approximately as below..
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// Here, I want to pass the "<some callback fn here>" to async_write,
// instead of calling boost::bind here. How to do that?
}
};
So, my specific question is..
What should my sendMsg signature look like? I am unable to arrive at the fn signature, especially the callback part.
Thanks to anyone letting me know how to do this.
If you have access to lambdas, it's easy
class Util {
public:
template <typename _Proc>
void sendMsg(const Msg& m, _Proc proc)
{
shared_ptr<Util> pThis = shared_from_this;
boost::asio::async_write(socket_, boost::asio::buffer(message_),
[&proc, =pThis/* , etra args */](boost::...::error_code ec, size_t bytes) // <- use capture to pass extra agruments and shared ptr
{
// use pThis !
proc(/* etra args */);
// or pthis->proc()
});
}
};
This will accept lambdas as well as std/boost::function

SEG fault in async_read_until()

I am facing an issue in reading through boost::asio::async_read_until(). The server code I have been using is as follows:
boost::asio::streambuf buffer;
boost::asio::async_read_until(socket_,buffer,DELIMITER,boost::bind(
&chat_session::handle_read_body,
shared_from_this(),
placeholders::error,
placeholders::bytes_transferred()
)
);
void chat_session::handle_read_body(const boost::system::error_code& error,std::size_t bytes_transferred)
{
FILE_LOG(logINFO)<<"Entry chat_session::handle_read_body\n";
FILE_LOG(logINFO)<<"Bytes Received:\t"<<bytes_transferred<<endl;
}
When my client send some data with delimiter, the server comes up with a dialog for segmentation fault and the file in the background opened is read_until.hpp.The control does not come to the handle_read_body() function.
Please help!
shared_from_this(),
this is your problem - pardon the pun.
Without seeing the crash, one of two things is probably happening:
a) Your code turns shared_from_this() into an rvalue from which a pointer is obtained, the shared object is then unref'd and the object gets destroyed after the callback has been queued,
b) The callback handler is trying to use the value of shared_from_this() to populate this, which is illegal (it doesn't know to call shared_from_this().get())
You might want to try the following:
~chat_session()
{
LOG("~chat_session(%p)", this);
}
// ...
auto ptr = shared_from_this();
LOG("this=%p, shared=%p, cs=%p, aru", this, ptr, (chat_session*)ptr);
boost::asio::async_read_until(socket_,buffer,DELIMITER,boost::bind(
&chat_session::handle_read_body,
ptr,
placeholders::error,
placeholders::bytes_transferred()
)

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.

Asynchronous write to socket and user values (boost::asio question)

I'm pretty new to boost. I needed a cross platform low level C++ network API, so I chose asio. Now, I've successfully connected and written to a socket, but since I'm using the asynchronous read/write, I need a way to keep track of the requests (to have some kind of IDs, if you will). I've looked at the documentation/reference, and I found no way to pass user data to my handler, the only option I can think of is creating a special class that acts as a callback and keeps track of it's id, then pass it to the socket as a callback. Is there a better way? Or is the best way to do it?
The async_xxx functions are templated on the type of the completion handler. The handler does not have to be a plain "callback", and it can be anything that exposes the right operator() signature.
You should thus be able to do something like this:
// Warning: Not tested
struct MyReadHandler
{
MyReadHandler(Whatever ContextInformation) : m_Context(ContextInformation){}
void
operator()(const boost::system::error_code& error, std::size_t bytes_transferred)
{
// Use m_Context
// ...
}
Whatever m_Context;
};
boost::asio::async_read(socket, buffer, MyReadHander(the_context));
Alternatively, you could also have your handler as a plain function and bind it at the call site, as described in the asio tutorial. The example above would then be:
void
HandleRead(
const boost::system::error_code& error,
std::size_t bytes_transferred
Whatever context
)
{
//...
}
boost::asio::async_read(socket, buffer, boost::bind(&HandleRead,
boost::asio::placeholders::error_code,
boost::asio::placeholders::bytes_transferred,
the_context
));