我已经为此做了很长时间,但没有成功。
想象一下,你的主要功能是这样的:
bool running = true;
int i = 0;
//waitHandler();
while(running)
i++;
现在我想添加并调用一个计时器,它将running设置为false,到期时。
void waitHandler(){
boost::asio::io_service timerService;
//create and bind the timer
boost::asio::deadline_timer timer(timerService,
boost::posix_time::milliseconds(2000));
timer.wait();
running = true;
cout<<"WaitHandler triggered"<<endl;
}
当然,这是行不通的(当你取消注释上面的注释时),因为定时器将阻塞主线程。如果我想在不阻塞主功能的情况下拥有此功能,该怎么办。
编辑:
//transfer some error message
void set_result(boost::system::error_code* a, boost::system::error_code b,deadline_timer &timer)
{
a->assign(b.value(),b.category());
}
template<class SOCKET>
void read_with_timeout(SOCKET & sock, unsigned int delay,
const asio::mutable_buffers_1& buffers)
{
//create error messages
boost::system::error_code timer_result;
boost::system::error_code read_result;
//initialize timer
deadline_timer timer(sock.get_io_service());
timer.expires_from_now(boost::posix_time::milliseconds(delay));
timer.async_wait(boost::bind(set_result, &timer_result, _1,boost::ref(timer)));
//initialize receive mechanism
sock.async_receive(buffers, boost::bind(set_result, &read_result, _1,boost::ref(timer)));
sock.get_io_service().reset();
//should run for one handler
while (sock.get_io_service().run_one())
{
if (read_result.value()==0){ //zero stands for, that the message was received properly.
timer.cancel();
//cout<<"Message received: => Timer cancelled => RETURN!"<<endl;
return;
}
if(timer.expires_from_now().total_milliseconds() <=0){
sock.cancel();
//cout<<"Timeout => Socket cancelled => RETURN!"<<endl;
return;
}
}
}
正如所说,这几乎表明了人们所希望的行为,但也有一些问题:
- 为什么即使使用
run_one
,也可以触发计时器的处理程序和接收的处理程序 - 当接收到0字节时,为什么接收也会激发。对我来说,这听起来像是什么都没有收到,功能应该等待
- 这是正确的方法吗?正如我所说,我想接收或暂停。(像ping)
事实上,当数据包出现在Wireshark中时,它们的接收顺序是错误的——我想这与async_receive
有关,它并不真正等待传入消息,只是在函数调用之前占用缓冲区中的内容
该怎么办?
你让这件事变得比需要的复杂得多。这个网站上有很多关于超时的问题,Boost.Asio网站上有一个很棒的例子。async_tcp_client示例中的注释有一个极好的ASCII图来解释这个场景
// This class manages socket timeouts by applying the concept of a deadline.
// Some asynchronous operations are given deadlines by which they must complete.
// Deadlines are enforced by an "actor" that persists for the lifetime of the
// client object:
//
// +----------------+
// | |
// | check_deadline |<---+
// | | |
// +----------------+ | async_wait()
// | |
// +---------+
//
// If the deadline actor determines that the deadline has expired, the socket
// is closed and any outstanding operations are consequently cancelled.
//
// Connection establishment involves trying each endpoint in turn until a
// connection is successful, or the available endpoints are exhausted. If the
// deadline actor closes the socket, the connect actor is woken up and moves to
// the next endpoint.
//
// +---------------+
// | |
// | start_connect |<---+
// | | |
// +---------------+ |
// | |
// async_- | +----------------+
// connect() | | |
// +--->| handle_connect |
// | |
// +----------------+
// :
// Once a connection is :
// made, the connect :
// actor forks in two - :
// :
// an actor for reading : and an actor for
// inbound messages: : sending heartbeats:
// :
// +------------+ : +-------------+
// | |<- - - - -+- - - - ->| |
// | start_read | | start_write |<---+
// | |<---+ | | |
// +------------+ | +-------------+ | async_wait()
// | | | |
// async_- | +-------------+ async_- | +--------------+
// read_- | | | write() | | |
// until() +--->| handle_read | +--->| handle_write |
// | | | |
// +-------------+ +--------------+
//
// The input actor reads messages from the socket, where messages are delimited
// by the newline character. The deadline for a complete message is 30 seconds.
//
// The heartbeat actor sends a heartbeat (a message that consists of a single
// newline character) every 10 seconds. In this example, no deadline is applied
// message sending.
//
您应该努力在应用程序中实现类似的设计。没有必要像你在问题中发布的那样,通过编写read_with_timeout()
函数来敷衍了事。使用async_read()
、async_write()
和async_wait()
就足以为您提供所需的功能。
我认为你的困惑部分源于线程。不要去想,先了解基本概念。您将希望使用单个线程(调用main()
的线程)和单个io_service
来启动。之后,您可以探索更高级的概念。如果您试图将这些代码集成到一个更大的应用程序中,那就完全是另一个问题了。
研究积极分子的设计模式可能对你也有帮助。
您可以在一个单独的线程中执行io_service::run
(并以某种方式同步对running
的访问),也可以在while
循环中使用run_one()
/poll()
/poll_one()
手动泵送io_service
循环-无论您的情况如何。
我找到了某种解决方案。即使有些事情我不理解,我也能接受。
//transfer some error message
void set_result(boost::system::error_code* a, boost::system::error_code b,deadline_timer &timer)
{
a->assign(b.value(),b.category());
}
template<class SOCKET>
void read_with_timeout(SOCKET & sock, unsigned int delay,
const asio::mutable_buffers_1& buffers)
{
//create error messages
boost::system::error_code timer_result;
boost::system::error_code read_result;
//initialize timer
deadline_timer timer(sock.get_io_service());
timer.expires_from_now(boost::posix_time::milliseconds(delay));
timer.async_wait(boost::bind(set_result, &timer_result, _1,boost::ref(timer)));
//initialize receive mechanism
sock.async_receive(buffers, boost::bind(set_result, &read_result, _1,boost::ref(timer)));
sock.get_io_service().reset();
//should run for one handler
while (sock.get_io_service().run_one())
{
if (read_result.value()==0){ //zero stands for, that the message was received properly.
timer.cancel();
//cout<<"Message received: => Timer cancelled => RETURN!"<<endl;
return;
}
if(timer.expires_from_now().total_milliseconds() <=0){
sock.cancel();
//cout<<"Timeout => Socket cancelled => RETURN!"<<endl;
return;
}
}
}
这实际上适用于我的案例,取自http://lists.boost.org/Archives/boost/2007/04/120339.php这个线程引用了:如何在boost-asio中设置阻塞套接字的超时?
我刚刚将其调整为Boost 1.51。
一些事情对我来说仍然模糊不清,比如
- io_service.run_one实际上仍然会触发更多的事件处理程序,即使它应该只触发一个
- 此外,计时器中的一些事件我根本不感兴趣。我只想捕捉超时,而不是其他东西。(我不知道为什么还有其他东西)
无论如何,到目前为止我的问题已经解决了。
您必须在自己的线程上生成计时器,然后确保保护运行的变量不受并发访问。