I faced with periodical infinite waiting for handler call after boost::asio::async_read_until. This appears in two situations:
Given that there is not every time, it seems to me that I using async_read_until, async_write and boost::asio::strand incorrectly.
Here is the simplified server's schema:
(Some commands to server don't required a response, and sometimes it's requred to send a command to the client after some event on server, ie without obtaining data from the client. It is worth to clarify that on the same socket never run simultaneously more than one async_read_until operation and more than one async_write operation.)
And the client's one:
Server's code:
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
class server_session
: public boost::enable_shared_from_this<server_session>
{
public:
server_session(boost::asio::io_service& io_service)
: _io_service(io_service)
, _socket(io_service)
, _strand(io_service)
, _delimiter('\b')
{}
boost::asio::ip::tcp::socket& socket()
{
return _socket;
}
void read()
{
boost::asio::async_read_until(
_socket,
_streambuf,
_delimiter,
_strand.wrap(
boost::bind(
&server_session::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
)
);
}
void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
if (ec)
{
std::cerr << "async_read_until error: " << ec.message() << std::endl;
return;
}
std::istream is(&_streambuf);
std::string msg;
std::getline(is, msg, _delimiter);
read();
_io_service.post(
boost::bind(
&server_session::proc_msg,
shared_from_this(),
msg
)
);
}
void proc_msg(const std::string msg)
{
// command proc here
std::cout << "received: " << msg << std::endl;
static std::uint64_t i = 0;
write("resp_" + std::to_string(i++));
}
void write(const std::string& msg)
{
_strand.post(
boost::bind(
&server_session::write_impl,
shared_from_this(),
boost::shared_ptr<std::string>(new std::string(msg + _delimiter))
)
);
}
private:
void write_impl(const boost::shared_ptr<std::string>& text_ptr)
{
boost::asio::async_write(
_socket,
boost::asio::buffer(text_ptr->data(), text_ptr->size()),
_strand.wrap(
boost::bind(
&server_session::handle_write,
shared_from_this(),
text_ptr,
boost::asio::placeholders::error
)
)
);
}
void handle_write(const boost::shared_ptr<std::string>, const boost::system::error_code& ec)
{
if (ec)
{
std::cerr << "async_write error: " << ec.message() << std::endl;
}
}
boost::asio::io_service& _io_service;
boost::asio::ip::tcp::socket _socket;
boost::asio::strand _strand;
boost::asio::streambuf _streambuf;
char _delimiter;
};
class server
{
public:
server(int port)
: _acceptor(
_io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),port)
)
{
start_accept();
boost::thread_group thd_grp;
for (int i = 0; i < 2; ++i)
{
thd_grp.create_thread(
boost::bind(&boost::asio::io_service::run, &_io_service)
);
}
thd_grp.join_all();
}
private:
void start_accept()
{
_session_ptr.reset(new server_session(_io_service));
_acceptor.async_accept(
_session_ptr->socket(),
boost::bind(
&server::handle_accept,
this,
boost::asio::placeholders::error
)
);
}
void server::handle_accept(const boost::system::error_code& ec)
{
if (ec)
{
std::cerr << "handle_accept error: " << ec.message() << std::endl;
return;
}
_session_ptr->read();
start_accept();
}
boost::asio::io_service _io_service;
boost::asio::ip::tcp::acceptor _acceptor;
boost::shared_ptr<server_session> _session_ptr;
};
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "usage: " << argv[0] << " <port>";
return EXIT_FAILURE;
}
server s(std::stoi(argv[1]));
return EXIT_SUCCESS;
}
Client's code:
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
class client
{
public:
client(const std::string& host, const std::string& port)
: _socket(_io_service)
, _resolver(_io_service)
, _query(host, port)
, _delimiter('\b')
{
_iterator = _resolver.resolve(_query);
boost::asio::async_connect(
_socket,
_iterator,
boost::bind(
&client::handle_connect,
this,
boost::asio::placeholders::error
)
);
_io_service.run();
}
void handle_connect(const boost::system::error_code& ec)
{
if (ec)
{
std::cerr << "async_connect error: " << ec.message() << std::endl;
return;
}
write();
}
void write()
{
static std::uint64_t i = 0;
boost::shared_ptr<std::string> msg_ptr(new std::string("req_" + std::to_string(i++) + _delimiter));
boost::asio::async_write(
_socket,
boost::asio::buffer(msg_ptr->data(), msg_ptr->size()),
boost::bind(
&client::handle_write,
this,
msg_ptr,
boost::asio::placeholders::error
)
);
}
void handle_write(const boost::shared_ptr<std::string>, const boost::system::error_code& ec)
{
if (ec)
{
std::cerr << "async_write error: " << ec.message() << std::endl;
return;
}
read();
}
void read()
{
boost::asio::async_read_until(
_socket,
_streambuf,
_delimiter,
boost::bind(
&client::handle_read,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
if (ec)
{
std::cerr << "async_read_until error: " << ec.message() << std::endl;
return;
}
std::istream is(&_streambuf);
std::string msg;
std::getline(is, msg, _delimiter);
std::cout << "received: " << msg << std::endl;
write();
}
boost::asio::io_service _io_service;
boost::asio::ip::tcp::socket _socket;
boost::asio::ip::tcp::resolver _resolver;
boost::asio::ip::tcp::resolver::query _query;
boost::asio::ip::tcp::resolver::iterator _iterator;
boost::asio::streambuf _streambuf;
char _delimiter;
};
int main(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "usage: " << argv[0] << " <host> <port>";
return EXIT_FAILURE;
}
client c(argv[1], argv[2]);
return EXIT_SUCCESS;
}
Is it correct to async_write while async_read_until on the socket is already running, whereas strand is used?
Is it possible, that async_read_until somehow internally blocks the socket and in fact, data is not sent to the client?
I shall be grateful for any advices why the code may not work properly
I'm using boost 1.58, platforms - Win7 and Win8. Tested on localhost and LAN with the above result.
I can tell you at the very least you should be using asio::deadline_timer and resetting the timer every time you start a new async operation. You should especially be doing this with operations like async_read_until, because you're specifying conditions for the completion of the call which may never, ever be met. Look up the deadline_timer examples and implement an async_wait on every single one of your async ops, specifying a deadline for that operation. In the timeout operation, you can cancel the pending async operations, error out, or restart, whatever is appropriate.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.