我的程序总是使用依赖于平台的同步接收,它会阻止执行直到超时或接收事件,如:
recv(buf, size, timeout);
现在我想用boost替换这个代码以使其跨平台。我找到了解决方案,但我觉得这很难看(与单一函数调用相比)。我写了这个:
void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e)
{
if (e.value() == boost::system::errc::success) {
f_recv_timeout = true;
asio_socket.cancel();
}
}
void IPV4_HOST::recv_handler(const boost::system::error_code & , size_t bytes)
{
recv_timer.cancel();
bytes_received = bytes;
}
int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout)
{
f_recv_timeout = false;
bytes_received = 0;
recv_timer.expires_from_now(timeout.get_boost_milli());
recv_timer.async_wait(boost::bind(&IPV4_HOST::recv_timer_handler, this, boost::asio::placeholders::error));
asio_socket.async_read_some(boost::asio::buffer(buf, buf_size),
boost::bind(&IPV4_HOST::recv_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
io_service.run();
if (f_recv_timeout)
throw IO::TimeoutReceiveException();
return bytes_received;
}
你能告诉我,我做得对吗?有更简单的方法吗?
答案 0 :(得分:5)
这是正确的方向,但有一些微妙的要点需要考虑:
io_service
,则IPV4_HOST::receive()
将开始处理该作品。io_service::run()
在正常情况下返回时,暗示io_service
已停止。除非io_service为reset()
,否则对run()
,run_one()
,poll()
或poll_one()
的后续调用将立即返回。deadline_timer::cancel()
的备注部分强调了这种行为;但是,所有异步操作都显示此behavior。在现有代码中,当IO::TimeoutReceiveException
大于零时,这会导致bytes_received
被抛出。处理io_service
详细信息的一种解决方案,以及使用完成处理程序执行的非确定性顺序,可能如下所示:
void IPV4_HOST::recv_timer_handler(const boost::system::error_code & e)
{
timer_handled = true;
if (!e) {
f_recv_timeout = true;
asio_socket.cancel();
}
}
void IPV4_HOST::recv_handler(const boost::system::error_code &,
size_t bytes)
{
recv_handled = true;
recv_timer.cancel();
bytes_received = bytes;
}
int IPV4_HOST::receive(void * buf, size_t buf_size, TIME::INTERVAL timeout)
{
timer_handled = false;
recv_handled = false;
f_recv_timeout = false;
bytes_received = 0;
recv_timer.expires_from_now(timeout.get_boost_milli());
recv_timer.async_wait(
boost::bind(&IPV4_HOST::recv_timer_handler, this,
boost::asio::placeholders::error));
asio_socket.async_read_some(
boost::asio::buffer(buf, buf_size),
boost::bind(&IPV4_HOST::recv_handler, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// If a handler has not ran, then keep processing work on the io_service.
// We need to consume both handlers so that old handlers are not in the
// io_service the next time receive is called.
while (!timer_handled || !recv_handled)
{
io_service.run_one();
}
// If the io_service has stopped (due to running out of work), then reset
// it so that it can be run on next call to receive.
if (io_service.stopped())
io_service.reset();
// If no bytes were received and the timeout occurred, then throw. This
// handles the case where both a timeout and receive occurred at the
// same time.
if (!bytes_received && f_recv_timeout)
throw IO::TimeoutReceiveException();
return bytes_received;
}
此外,在尝试获取跨平台行为时,请阅读basic_stream_socket::cancel()
的注释。有一些平台特定的行为需要注意。