Reputation: 13
I am quite new in boost::asio and I have a problem. I am writting client that sends in loop some commands to server. I am sending command with boost::asio::async_write and I expect that every time I send commands handler will be called. In fact only during first sending I see that handler is called. My client looks like that:
Client::Client(boost::asio::io_service & p_ioService,
boost::asio::ip::tcp::endpoint p_endpoint)
: io_service(p_ioService), endpoint(p_endpoint), socket(p_ioService)
{
socket.connect(endpoint);
}
Client::~Client()
{
socket.close();
}
void Client::sendCommand(const string & p_command)
{
boost::asio::async_write(socket,boost::asio::buffer(p_command),
boost::bind(&Client::onSendingFinished,this, _1, _2));
io_service.run();
}
void Client::onSendingFinished(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
cout<<"Sent "<<bytes_transferred<<endl;
}
There is no other place in main.cpp where io_service.run is called. I notice that if I call io_service.reset() after io_service.run() it works fine, handler is called every time.
How should I solve this without io_service.reset()
Thanks in advance
Upvotes: 0
Views: 1897
Reputation: 4539
It's quite unusual to just send messages, it's far more common to have two way communication.
If you implemented a receiver as well, then your receive code would always require a receive handler running in the io_service
and you wouldn't have this problem...
Upvotes: 0
Reputation: 51871
I do not understand the aversion to calling io_service::reset()
. In this case, it is necessary to invoke prior to any subsequent calls to io_service::run()
:
reset()
must be called prior to any second or later set of invocations of therun()
,run_one()
,poll()
orpoll_one()
functions when a previous invocation of these functions returned due to theio_service
being stopped or running out of work.
It is possible that a thread returns from run()
as a result of an exception being thrown, yet the io_service
has neither been stopped nor ran out of work. In this case, the thread can invoke run()
without calling reset()
.
The current Client::sendCommand()
is synchronous. It is an implementation detail that it initiates an asynchronous operation, then blocks in io_service::run()
waiting for the operation to complete. Unless there are multiple threads invoking commands on socket
, multiple threads running the io_service
, or the write operation needs to be cancellable, such as from a timeout, then it would be functionally equivalent and possible easier to implement Client::sendCommand()
with a synchronous write()
.
void Client::sendCommand(const string & p_command)
{
boost::system::error_code ec;
std::size_t bytes_transferred =
boost::asio::write(socket, boost::asio::buffer(p_command), ec);
onSendingFinished(ec, bytes_transferred);
}
If Client::sendCommand()
needs to be asynchronous, then:
io_service
should be ran from outside of Client::sendCommand()
. If the io_service
does not always have outstanding work, then io_service::work
can be used control when run()
returns. See this answer for more details as to when io_service::run()
blocks and unblocks.The underlying memory provided to async_write()
as the buffer (p_command
) needs to remain valid until the operation's handler, Client::onSendingFinished()
, has been called. In this case, it may require making a copy of p_command
in Client::sendCommand()
, writing the copy to the socket, then deleting the copy from within the handler.
[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
Upvotes: 1
Reputation: 1404
While it is not inherently bad to call reset()
every now and then, there are two typical ways to avoid having to do it.
Start a new async operation within the handler of the first one. run()
only returns once all handlers have finished and thus a new async operation started in a handler is still in time to keep blocking io_service
.
Use io_service::work
. If you create an instance of io_service::work
constructed with your io_service
as parameter, than your subsequent calls to run()
will not return as long as the work
object remains alive. And thus you will not have to reset anything. Of course this means that either one of your handlers or another thread has to destroy the work
object at some time, if you want run()
to ever stop blocking.
Upvotes: 1