I'm learning how to use Boost:asio library with Serial Port. I wrote some code using synchrous write and read and I now want to use asynchrous but it's not working.
Simple Example:
void readHandler(const boost::system::error_code&,std::size_t);
streambuf buf;
int main(int argc,char *argv[]){
io_service io;
serial_port port(io,PORT);
if(port.isopen()){
while(1){
// ... getting std::string::toSend from user ...
write(port,buffer(toSend.c_str(),toSend.size()));
async_read_until(port,buf,'\n',readHandler); // <= it's returning but not calling readHandler at all
}
port.close();
}
}
void readHandler(const boost::system::error_code& error,std::size_t bytes_transferred){
std::cout << "readHandler()" << std::endl;
//... reading from buf object and calling buf.consume(buf.size()) ...
}
async_read_until() it's returning but not calling readHandler(). If I change to synchrous read, it's reading from port OK. I also checking buf object each while loop and it's empty. What I'm doing wrong ??
As Janm has pointed out you need to call io.run for the async_read_until to work.
But...
You also need to convert the write over to an async_write, as the sync and async calls don't really work well together within asio. What you would need to do is the following:
setup first async_write
call io.run
in your write handler setup the async_read_until
in your read handler setup the next async_write
You need to call the run() method on the io_service for the callbacks to work. In your loop, you need io.run().
Related
I have a program (client + server) that works with no issue with this write:
boost::asio::write(this->socket_, boost::asio::buffer(message.substr(count,length_to_send)));
where socket_ is boost::asio::ssl::stream<boost::asio::ip::tcp::socket> and message is an std::string.
I would like to make this better and non-blocking, so I created a function that could replace this, it's called like follows:
write_async_sync(socket_,message.substr(count,length_to_send));
The purpose of this function is:
To make the call async, intrinsically
To keep the interface unchanged
The function I implemented simply uses promise/future to simulate sync behavior, which I will modify later (after it works) to be cancellable:
std::size_t
SSLClient::write_async_sync(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket,
const std::string& message_to_send)
{
boost::system::error_code write_error;
std::promise<std::size_t> write_promise;
auto write_future = write_promise.get_future();
boost::asio::async_write(socket,
boost::asio::buffer(message_to_send),
[this,&write_promise,&write_error,&message_to_send]
(const boost::system::error_code& error,
std::size_t size_written)
{
logger.write("HANDLING WRITING");
if(!error)
{
write_error = error;
write_promise.set_value(size_written);
}
else
{
write_promise.set_exception(std::make_exception_ptr(std::runtime_error(error.message())));
}
});
std::size_t size_written = write_future.get();
return size_written;
}
The problem: I'm unable to get the async functionality to work. The sync one works fine, but async simply freezes and never enters the lambda part (the writing never happens). What am I doing wrong?
Edit: I realized that using poll_one() makes the function execute and it proceeds, but I don't understand it. This is how I'm calling run() for io_service (before starting the client):
io_service_work = std::make_shared<boost::asio::io_service::work>(io_service);
io_service_thread.reset(new std::thread([this](){io_service.run();}));
where basically these are shared_ptr. Is this wrong? Does this way necessitate using poll_one()?
Re. EDIT:
You have the io_service::run() correctly. This tells me you are blocking on the future inside a (completion) handler. That, obviously, prevents run() from progressing the event loop.
The question asked by #florgeng was NOT whether you have an io_service instance.
The question is whether you are calling run() (or poll()) on it suitably for async operations to proceed.
Besides, you can already use future<> builtin:
http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/overview/cpp2011/futures.html
Example: http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/example/cpp11/futures/daytime_client.cpp
std::future<std::size_t> recv_length = socket.async_receive_from(
boost::asio::buffer(recv_buf),
sender_endpoint,
boost::asio::use_future);
My Program acts as a server to which a client can connect. Once a client connected, he will get updates from the server every ~5 seconds. This is the write-function that is called every 5 seconds to send the new data to the client:
void NIUserSession::write(std::string &message_orig)
{
std::cout << "Writing message" << std::endl;
std::shared_ptr<std::string> message = std::make_shared<std::string>( message_orig );
message->append("<EOF>");
boost::system::error_code ec;
boost::asio::async_write(this->socket_, boost::asio::buffer(*message),
boost::asio::transfer_all(), boost::bind(&NIUserSession::writeHandler,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred(),
message
));
}
void NIUserSession::writeHandler(const boost::system::error_code &error, std::size_t bytes_transferred, std::shared_ptr<std::string> message)
{
std::cout << "Write Handler" << std::endl;
if(error)
{
std::cout << "Write handler error: " << error.message() << std::endl;
this->disconnect();
}
}
void NIUserSession::disconnect()
{
std::cout << "Disconnecting client, cancling all write and read operations." << std::endl;
this->socket_.lowest_layer().cancel();
delete this;
}
If there is an error in the write operations the connection between the server and the client gets closed and all async operations are cancled (this->socket_.lowest_layer().cancel();).
The problem is that if the connection times out, writeHandler will not be called immediately. Instead, the write operations "stack up" until the first one reaches writeHandler.
This should be the normal output of the program:
Writing message
Write Handler
... Other stuff ...
... Other stuff ...
Writing message
Write Handler
If the connections times out, this is what happens:
Writing message
Write Handler
Write handler error: Connection timed out
Disconnecting client, cancling all write and read operations.
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Segmentation fault
At the end, a segmentation fault rises. I think this is because disconnectis called while other async operations are still on their way.
I thought I could avoid it by using this->socket_.lowest_layer().cancel(); directly after the first async operation fails, but it doesn't work.
How can I avoid a segmentation fault?
Well, you should not delete this when cancelling the operations since the callbacks for the pending I/O operations will still be invoked and then accessing this leads to undefined behavior. There are multiple ways to tackle this:
Don't write data until you actually know that previous data has been written. You could queue the std::string instances passed to NIUserSession::write in case an outstanding write is still pending and then actually write them in the handler when the outstanding write operation completes. That way you will not have multiple I/O operations in flight.
Inherit from std::enable_shared_from_this and pass shared_from_this() instead of this to the async_write call (this is what the Boost asynchronous TCP daytime server example does). That way pending I/O operations will keep a reference to your class and the destructor will be called if all of them complete.
I'm implementing a tcp server with boost asio library.
In the server, I use asio::async_read_some to get data, and use asio::write to write data. The server code is something like that.
std::array<char, kBufferSize> buffer_;
std::string ProcessMessage(const std::string& s) {
if (s == "msg1") return "resp1";
if (s == "msg2") return "resp2";
return "";
}
void HandleRead(const boost::system::error_code& ec, size_t size) {
std::string message(buffer_.data(), size);
std::string resp = ProcessMessage(message);
if (!resp.empty()) {
asio::write(socket, boost::asio::buffer(message), WriteCallback);
}
socket.async_read_some(boost::asio::buffer(buffer_));
}
Then I write a client to test the server, the code is something like
void MessageCallback(const boost::system::error_code& ec, size_t size) {
std::cout << string(buffer_.data(), size) << std::endl;
}
//Init socket
asio::write(socket, boost::asio::buffer("msg1"));
socket.read_some(boost::asio::buffer(buffer_), MessageCallback);
// Or async_read
//socket.async_read_some(boost::asio::buffer(buffer_), MessageCallback);
asio::write(socket, boost::asio::buffer("msg1"));
socket.read_some(boost::asio::buffer(buffer_), MessageCallback);
// Or async_read
//socket.async_read_some(boost::asio::buffer(buffer_), MessageCallback);
If I run the client, the code will be waiting at second read_some, and output is:resp1.
If I remove the first read_some, the ouput is resp1resp2, that means the server done the right thing.
It seems the first read_some EAT the second response but don't give the response to MessageCallback function.
I've read the quesion at What is a message boundary?, I think if this problem is a "Message Boundary" problem, the second read_some should print something as the first read_some only get part of stream from the tcp socket.
How can I solve this problem?
UPDATE:
I've try to change the size of client buffer to 4, that output will be:
resp
resp
It seems the read_some function will do a little more than read from the socket, I'll read the boost code to find out is that true.
The async_read_some() member function is very likely not doing what you intend, pay special attention to the Remarks section of the documentation
The read operation may not read all of the requested number of bytes.
Consider using the async_read function if you need to ensure that the
requested amount of data is read before the asynchronous operation
completes.
Note that async_read() free function does offer the guarantee that you are looking for
This operation is implemented in terms of zero or more calls to the
stream's async_read_some function, and is known as a composed
operation. The program must ensure that the stream performs no other
read operations (such as async_read, the stream's async_read_some
function, or any other composed operations that perform reads) until
this operation completes.
I want to create an autonomous thread devoted only to receive data from an UDP socket using boost libraries (asio). This thread should be an infinite loop triggered by some data received from the UDP socket. In my application I need to use an asynchronous receive operation.
If I use the synchronous function receive_from everything works as expected.
However if I use async_receive_from the handler is never called. Since I use a semaphore to detect that some data have been received, the program locks and the loop is never triggered.
I have verified (with a network analyzer) that the sender device properly sends the data on the UDP socket.
I have isolated the problem in the following code.
#include <boost\array.hpp>
#include <boost\asio.hpp>
#include <boost\thread.hpp>
#include <boost\interprocess\sync\interprocess_semaphore.hpp>
#include <iostream>
typedef boost::interprocess::interprocess_semaphore Semaphore;
using namespace boost::asio::ip;
class ReceiveUDP
{
public:
boost::thread* m_pThread;
boost::asio::io_service m_io_service;
udp::endpoint m_local_endpoint;
udp::endpoint m_sender_endpoint;
udp::socket m_socket;
size_t m_read_bytes;
Semaphore m_receive_semaphore;
ReceiveUDP() :
m_socket(m_io_service),
m_local_endpoint(boost::asio::ip::address::from_string("192.168.0.254"), 11),
m_sender_endpoint(boost::asio::ip::address::from_string("192.168.0.11"), 5550),
m_receive_semaphore(0)
{
Start();
}
void Start()
{
m_pThread = new boost::thread(&ReceiveUDP::_ThreadFunction, this);
}
void _HandleReceiveFrom(
const boost::system::error_code& error,
size_t received_bytes)
{
m_receive_semaphore.post();
m_read_bytes = received_bytes;
}
void _ThreadFunction()
{
try
{
boost::array<char, 100> recv_buf;
m_socket.open(udp::v4());
m_socket.bind(m_local_endpoint);
m_io_service.run();
while (1)
{
#if 1 // THIS WORKS
m_read_bytes = m_socket.receive_from(
boost::asio::buffer(recv_buf), m_sender_endpoint);
#else // THIS DOESN'T WORK
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
/* The program locks on this wait since _HandleReceiveFrom
is never called. */
m_receive_semaphore.wait();
#endif
std::cout.write(recv_buf.data(), m_read_bytes);
}
m_socket.close();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
};
void main()
{
ReceiveUDP receive_thread;
receive_thread.m_pThread->join();
}
A timed_wait on the semaphore is to be preferred, however for debug purposes I have used a blocking wait as in the code above.
Did I miss something? Where is my mistake?
Your call to io_service.run() is exiting because there is no work for the io_service to do. The code then enters the while loop and calls m_socket.async_receive_from. At this point the io_service is not running ergo it never reads the data and calls your handler.
you need to schedule the work to do before calling io_service run:
ie:
// Configure io service
ReceiveUDP receiver;
m_socket.open(udp::v4());
m_socket.bind(m_local_endpoint);
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
The handler function will do the following:
// start the io service
void HandleReceiveFrom(
const boost::system::error_code& error,
size_t received_bytes)
{
m_receive_semaphore.post();
// schedule the next asynchronous read
m_socket.async_receive_from(
boost::asio::buffer(recv_buf),
m_sender_endpoint,
boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
m_read_bytes = received_bytes;
}
Your thread then simply waits for the semaphore:
while (1)
{
m_receive_semaphore.wait();
std::cout.write(recv_buf.data(), m_read_bytes);
}
Notes:
Do you really need this additional thread? The handler is completely asynchronous, and boost::asio can be used to manage a thread pool (see: think-async)
Please do not use underscores followed by a capitol letter for variable / function names. They are reserved.
m_io_service.run() returns immediately, so noone dispatches completion handlers. Note that io_service::run is a kind of "message loop" of an asio-based application, and it should run as long as you want asio functionality to be available (this's a bit simplified description, but it's good enough for your case).
Besides, you should not invoke async.operation in a loop. Instead, issue subsequent async.operation in the completion handler of the previous one -- to ensure that 2 async.reads would not run simultaniously.
See asio examples to see the typical asio application design.
Maybe I am misunderstanding how things work, but I am trying to add a timeout to a read_until call, so I created a deadline_timer and started it before calling read_until, but the read_until still blocks everything, and the timer never gets activated. Am I doing it wrong? Below are some snippets from my code.
void MyClass::handle_timeout(const boost::system::error_code& error)
{
// Our deadline timer went off.
std::cout << "Deadline Timer was triggered." << std::endl;
Disconnect();
}
// Read some data.
void MyClass::ReadData(){
boost::asio::streambuf response;
deadline_.expires_from_now(boost::posix_time::seconds(DEFAULT_TIMEOUT));
deadline_.async_wait(boost::bind(&MyClass::handle_timeout, this, _1));
boost::asio::read_until(socket_,response,asString);
}
you're misunderstanding how things work. If you desire cancelability, you need to use the asynchronous methods such as
boost::asio::async_read_until(...);
instead of
boost::asio::read_until(socket_,response,asString);