Trying to use deadline_timer to add timeout to read_until - c++

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

Related

ASIO async_read doesn't work while async_read_until works on server

Observation
I built a demo application according to this server example using ASIO after I used C++11 std to replace everything originally in boost. The server can show that class member tcp_session::start() is called only after the client connects which is good indication that the server accepts the connection from the client.
However, I saw nothing received by handle_read while the clients sends a lot of data. I got some std::cout in handle_read and stop. I put the timeout to be 6 seconds now and found this:
The start is called right after the client connects, and then nothing indicating that the handle_read is called, but after 6 seconds, stop() is called, and then handle_read is called because of the timeout and socket_.isOpen() is false.
Then I found that if I change async_read to async_read_until that was commented originally by me, then the handle_read will be called andthe socket_.isopen is true so I can really see the packets.
Question:
The delimiter was there but I don't want one. How do I async read a whole TCP string without a delimiter? Why async_read doesn't work? Should it work like this? Is there anything wrong in my code?
I am using VS2015 and test on localhost.
Answer
TCP doesn't have boundary so I decided to put special character to indicate the end of each packet.
Here are some relevant code:
class tcp_session : public subscriber, public std::enable_shared_from_this<tcp_session> {
public:
void start() {
std::cout<<"started"<<std::endl;
channel_.join(shared_from_this());
start_read();
input_deadline_.async_wait(
std::bind(&tcp_session::check_deadline, shared_from_this(), &input_deadline_)
);
await_output();
output_deadline_.async_wait(
std::bind(&tcp_session::check_deadline, shared_from_this(), &output_deadline_)
);
}
private:
bool stopped() const {
return !socket_.is_open();// weird that it is still not open
}
void start_read() {
// Set a deadline for the read operation.
input_deadline_.expires_from_now(timeout_); //was std::chrono::seconds(30) in example
char a = 0x7F;
// Start an asynchronous operation to read a 0x7F-delimited message or read all
//asio::async_read_until(socket_, input_buffer_, a, std::bind(&TCP_Session::handle_read, shared_from_this(), std::placeholders::_1));
asio::async_read(socket_, input_buffer_,
std::bind(&TCP_Session::handle_read, shared_from_this(), std::placeholders::_1));
}
void handle_read(const asio::error_code& ec) {
if (stopped()) // it thinks it stopped and returned without processing
return;

How to make asynchronous call with timeout

I want to make an asynchronous call in C++ with timeout, meaning I want to achieve sth like that.
AsynchronousCall(function, time);
if(success)
//call finished succesfully
else
//function was not finished because of timeout
EDIT : Where function is a method that takes a lot of time and I want to interrupt it when it takes too much time.
I' ve been looking for how to achieve it and I thinki boost::asio::deadline_timer is way to go. I guess calling timer.async_wait(boost::bind(&A::fun, this, args)) is what I need, but I do not know how to find if the call was success or was aborted due to timeout.
EDIT: after the answer from ForEveR my code now looks like this.
boost::asio::io_service service;
boost::asio::deadline_timer timer(service);
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait(boost::bind(&A::CheckTimer, this, boost::asio::placeholders::error));
boost::thread bt(&A::AsynchronousMethod, this, timer, args); //asynchronous launch
void A::CheckTimer(const boost::system::error_code& error)
{
if (error != boost::asio::error::operation_aborted)
{
cout<<"ok"<<endl;
}
// timer is cancelled.
else
{
cout<<"error"<<endl;
}
}
I wanted to pass the timer by reference and cancel it in the end of asynchronous method, but I got an error that I cannot access private member declared in class ::boost::asio::basic_io_object.
Maybe using the deadline timer is not that good idea ? I would really appreciate any help. I am passing the timer to the function, because the method that calls the asynchronous method is asynchronous itself and thus I cannot have one timer for whole class or sth like that.
You should use boost::asio::placeholders::error
timer.async_wait(boost::bind(
&A::fun, this, boost::asio::placeholders::error));
A::fun(const boost::system::error_code& error)
{
// timeout, or some other shit happens
if (error != boost::asio::error::operation_aborted)
{
}
// timer is cancelled.
else
{
}
}

Timeouts on read and writes

I have been searching for a way to cancel a Boost ASIO read or write operation if it takes over a certain amount of time. My server is sending out HTTP requests, and reading results from those requests, so I originally had coded it as a synchronous read/write, and if it took so long, I would just carry on and ignore the results when they came back. This caused a problem if a server went down, my server would open to many sockets, and would crash. So I decided that I wanted to cancel the read/write if there was too long of a delay, but apparently synchronous read/writes are not able to be canceled without destroying the thread they are running in, which I do not want to do. So I found a post about how to mimic a synchronous read/write with asynchronous calls and cancel a call on time out. This
is the post that I followed. I know this post is fairly old, so I am not sure if function calls have change since that version and the one I am working with(1.48), but this doesn't seem to be working quite right. Here is my code
bool connection::query_rtb(const std::string &request_information, std::string &reply_information)
{
try
{
boost::optional<boost::system::error_code> timer1_result, timer2_result, write_result, read_result;
boost::array<char,8192> buf;
buf.assign(0);
boost::asio::deadline_timer dt(io_service_);
dt.expires_from_now(boost::posix_time::milliseconds(100));
dt.async_wait(boost::bind(&connection::set_result, this, &timer1_result, _1, "timer1"));
boost::asio::async_write(socket_, boost::asio::buffer(request_information, request_information.size()), boost::bind(&connection::set_result, this, &write_result, _1, "write"));
io_service_.reset();
while(io_service_.run_one())
{
if(write_result)
{
dt.cancel();
}
else if(timer1_result)
{
socket_.cancel();
}
}
boost::asio::deadline_timer dt2(io_service_);
dt2.expires_from_now(boost::posix_time::milliseconds(3000));
dt2.async_wait(boost::bind(&connection::set_result, this, &timer2_result, _1, "timer2"));
boost::asio::async_read(socket_, boost::asio::buffer(buf), boost::bind(&connection::set_result, this, &read_result, _1, "read"));
//socket_.async_receive(boost::asio::buffer(buf), boost::bind(&connection::set_result, this, &read_result, _1, "read"));
io_service_.reset();
while(io_service_.run_one())
{
if(read_result)
{
dt2.cancel();
}
if(timer2_result)
{
socket_.cancel();
}
}
reply_information = buf.data();
std::cout << reply_information << std::endl;
return true;
}catch(std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void persistent_connection::set_result(boost::optional<boost::system::error_code> *a, boost::system::error_code ec, std::string t)
{
std::cout << t << std::endl;
a->reset(ec);
}
I was wondering if anyone see anything wrong with this code, or has any ideas on why it is not working. Currently the write seems to be fine, however the will not read until after the dt2 is done with it's timer. Please let me know if you need any more information, I will be glad to provide some.
Edit:
Seems like I got it working testing something I thought I previously tested. Using async_receive instead of async_read seems to have fixed whatever problem I was having. Any clue why this would cause I problem? I want to know if there is a problem with my logic or if that is how is async_read will usually act.
boost::array<char,8192> buf;
...
boost::asio::async_read(socket_, boost::asio::buffer(buf), boost::bind(&connection::set_result, this, &read_result, _1, "read"));
You have instructed your program to read 8192 bytes from the socket. If switching the logic from using the async_read() free function to the async_receive() member function resolves this problem, consult the documentation
Remarks
The receive operation may not receive 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 received before the asynchronous
operation completes.

Persistent ASIO connections

I am working on a project where I need to be able to use a few persistent to talk to different servers over long periods of time. This server will have a fairly high throughput. I am having trouble figuring out a way to setup the persistent connections correctly. The best way I could think of to do this is create a persistent connection class. Ideally I would connect to the server one time, and do async_writes as information comes into me. And read information as it comes back to me. I don't think I am structuring my class correctly though.
Here is what I have built right now:
persistent_connection::persistent_connection(std::string ip, std::string port):
io_service_(), socket_(io_service_), strand_(io_service_), is_setup_(false), outbox_()
{
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(ip,port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *iterator;
socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator));
io_service_.poll();
}
void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
if(ec)
{
std::cout << "Couldn't connect" << ec << std::endl;
return;
}
else
{
boost::asio::socket_base::keep_alive option(true);
socket_.set_option(option);
boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n", boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}
}
void persistent_connection::write(const std::string &message)
{
write_impl(message);
//strand_.post(boost::bind(&persistent_connection::write_impl, this, message));
}
void persistent_connection::write_impl(const std::string &message)
{
outbox_.push_back(message);
if(outbox_.size() > 1)
{
return;
}
this->write_to_socket();
}
void persistent_connection::write_to_socket()
{
std::string message = "GET /"+ outbox_[0] +" HTTP/1.0\r\n";
message += "Host: 10.1.10.120\r\n";
message += "Accept: */*\r\n";
boost::asio::async_write(socket_, boost::asio::buffer(message.c_str(), message.size()), strand_.wrap(
boost::bind(&persistent_connection::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
}
void persistent_connection::handle_write(const boost::system::error_code& ec, std::size_t bytes_transfered)
{
outbox_.pop_front();
if(ec)
{
std::cout << "Send error" << boost::system::system_error(ec).what() << std::endl;
}
if(!outbox_.empty())
{
this->write_to_socket();
}
boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n",boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}
The first message I will send from this seems to send out fine, the server gets it, and responds with a valid response. I see two problem unfortunately:
1) My handle_write is never called after doing the async_write command, I have no clue why.
2) The program never reads the response, I am guessing this is related to #1, since asyn_read_until is not called until that function happens.
3) I was also wondering if someone could tell me why my commented out strand_.post call would not work.
I am guessing most of this has to due with my lack of knowledge of how I should be using my io_service, so if somebody could give me any pointer that would be greatly appreciated. And if you need any additional information, I would be glad to provide some more.
Thank you
Edit call to write:
int main()
{
persistent_connection p("10.1.10.220", "80");
p.write("100");
p.write("200");
barrier b(1,30000); //Timed mutex, waits for 300 seconds.
b.wait();
}
and
void persistent_connection::handle_read_headers(const boost::system::error_code &ec)
{
std::istream is(&buf_);
std::string read_stuff;
std::getline(is,read_stuff);
std::cout << read_stuff << std::endl;
}
The behavior described is the result of the io_service_'s event loop no longer being processed.
The constructor invokes io_service::poll() which will run handlers that are ready to run and will not block waiting for work to finish, where as io_service::run() will block until all work has finished. Thus, when polling, if the other side of the connection has not written any data, then no handlers may be ready to run, and execution will return from poll().
With regards to threading, if each connection will have its own thread, and the communication is a half-duplex protocol, such as HTTP, then the application code may be simpler if it is written synchronously. On the other hand, if it each connection will have its own thread, but the code is written asynchronously, then consider handling exceptions being thrown from within the event loop. It may be worth reading Boost.Asio's
effect of exceptions thrown from handlers.
Also, persistent_connection::write_to_socket() introduces undefined behavior. When invoking boost::asio::async_write(), it is documented that the caller retains ownership of the buffer and must guarantee that the buffer remains valid until the handler is called. In this case, the message buffer is an automatic variable, whose lifespan may end before the persistent_connection::handle_write handler is invoked. One solution could be to change the lifespan of message to match that of persistent_connection by making it a member variable.

boost::asio::async_read_until not calling handler

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().