为什么 boost deadline_timer 用作 async_connect 的超时在 unix 上不起作用?
Why does boost deadline_timer used as timeout for async_connect not work on unix?
我有一个程序使用 boost asio 异步连接到 3 个 TCP 套接字,使用 deadline_timer
作为连接超时。在 windows 上一切正常。连接在 5 秒后超时。然而,在 Unix(Ubuntu on WSL,Linux Mint VM,macOS)上,connectDeadline 永远不会触发。 async_connect 操作永远运行下去。为什么这不起作用,我怎样才能使它在 Unix 上也起作用?
代码:
注意:connect 是从主线程(也是 GUI 线程)调用的。
#include "NetManager.h"
NetManager::NetManager(NetManagerListener& listener) : listener(listener),
connectDeadline(io),
socket1(io),
socket2(io),
socket3(io),
asioThread(&NetManager::handleAsioOperations, this){
}
NetManager::~NetManager() {
running = false;
io.stop();
asioThread.join();
}
void NetManager::connect(){
connectCounter = 0;
hasHandledConnectError = false;
socket1.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_1),
boost::bind(&NetManager::handleConnect, this, _1));
socket2.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_2),
boost::bind(&NetManager::handleConnect, this, _1));
socket3.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_3),
boost::bind(&NetManager::handleConnect, this, _1));
connectDeadline.expires_from_now(boost::posix_time::seconds(CONNECT_TIMEOUT));
connectDeadline.async_wait(boost::bind(&NetManager::handleConnectTimeout, this, _1));
}
void NetManager::disconnect(){
//NOTE: Close also cancels incomplete async operations
socket1.close();
socket2.close();
socket3.close();
}
////////////////////////////////////////////////////////////////////////
/// ASIO Handlers
////////////////////////////////////////////////////////////////////////
void NetManager::handleAsioOperations(){
while(running){
io.run(); // Run any async operations
}
}
void NetManager::handleConnect(const boost::system::error_code &ec){
// When connections are canceled the handler is called with operation_aborted. No need to respond to that.
if(ec && ec != boost::asio::error::operation_aborted && !hasHandledConnectError){
hasHandledConnectError = true; // Likely to be 3 repeated errors. Make sure to only handle the first one
cerr << "Connect Failed: " << ec.message() << endl;
connectDeadline.cancel(); // Don't fire the timeout
disconnect(); // Disconnect any already connected sockets
connectedToRobot = false;
listener.onConnect(false);
}else if (!ec){
connectCounter++;
}
if(connectCounter == 3){
cout << "Successful connect" << endl;
connectDeadline.cancel(); // Don't fire the timeout
connectedToRobot = true;
listener.onConnect(true);
}
}
void NetManager::handleConnectTimeout(const boost::system::error_code &ec){
if(ec != boost::asio::error::operation_aborted){
cerr << "Connect timed out." << endl;
disconnect(); // Disconnect any already connected sockets
connectedToRobot = false;
listener.onConnect(false);
}
}
编辑:
令人困惑的是,这在 Unix 操作系统上运行良好:
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <iostream>
#include <thread>
using namespace boost::asio;
using namespace boost::asio::ip;
int main(){
io_service io;
deadline_timer timer1(io);
tcp::socket sock(io);
timer1.expires_from_now(boost::posix_time::seconds(3));
sock.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8090), [](const boost::system::error_code &ec){
std::cout << "SocketError: " << ec.message() << std::endl;
});
timer1.async_wait([&](const boost::system::error_code &ec){
std::cout << "First timer" << std::endl;
sock.close();
});
std::thread worker([&](){
while(true){
io.run();
}
});
worker.detach();
while(true){} // Simulate the unavailable main (GUI) thread
}
输出:
First timer
SocketError: Operation canceled
好的,我在 Ubuntu 和 Windows 上测试了许多不同的场景后找到了答案。事实证明,如果调用 io_service.run
的线程是在异步任务(async_wait
、async_connect
)创建之前创建的,则使用线程连续调用 io_service::run
在 Unix 操作系统上不起作用开始了。如果工作线程是在调用 async_connect
和 async_wait
之前创建的,我发布的第二个示例会中断(在 unix 上不是 windows)。我刚刚修改了我的 NetManger class 以删除始终 运行 线程。
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <thread>
using namespace boost::asio;
using namespace boost::asio::ip;
class NetManager{
private:
io_service io;
tcp::socket socket1, socket2, socket3;
deadline_timer connectDeadline;
int connectCounter = 0;
void handleConnect(const boost::system::error_code &ec){
if(ec){
std::cout << "Connect error: " << ec.message() << std::endl;
connectDeadline.cancel();
disconnect();
}else
connectCounter++;
if(connectCounter == 3)
std::cout << "Connected" << std::endl;
}
void handleConnectTimeout(const boost::system::error_code &ec){
std::cout << "Timeout fired." << std::endl;
disconnect();
}
public:
void connect(){
connectCounter = 0;
connectDeadline.expires_from_now(boost::posix_time::seconds(5));
socket1.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8090), boost::bind(&NetManager::handleConnect, this, _1));
socket2.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8091), boost::bind(&NetManager::handleConnect, this, _1));
socket3.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8092), boost::bind(&NetManager::handleConnect, this, _1));
connectDeadline.async_wait(boost::bind(&NetManager::handleConnectTimeout, this, _1));
std::thread([&]{io.run();}).detach(); // Run the async operations on a separate thread
}
void disconnect(){
socket1.close();
socket2.close();
socket3.close();
}
NetManager(): connectDeadline(io),
socket1(io),
socket2(io),
socket3(io){
}
~NetManager(){
io.stop();
}
};
int main(){
NetManager manager;
manager.connect();
std::cout << "Trying to connect..." << std::endl;
while(true); // Simulate the busy main (GUI) thread.
}
boost::io_service::run
如果没有工作则停止。您不应该像这样在循环中调用它,因为正如文档所述:
A normal exit from the run() function implies that the io_service
object is stopped (the stopped() function returns true). Subsequent
calls to run(), run_one(), poll() or poll_one() will return
immediately unless there is a prior call to reset().
在第一个示例中调用 connect() 时没有显示,我假设不久之后,所以它是否有效取决于线程启动的速度和 run()
调用已执行。
您可以使用 boost::io_service::work
来规避此行为,这将防止 io_service
运行 停止工作。
我有一个程序使用 boost asio 异步连接到 3 个 TCP 套接字,使用 deadline_timer
作为连接超时。在 windows 上一切正常。连接在 5 秒后超时。然而,在 Unix(Ubuntu on WSL,Linux Mint VM,macOS)上,connectDeadline 永远不会触发。 async_connect 操作永远运行下去。为什么这不起作用,我怎样才能使它在 Unix 上也起作用?
代码: 注意:connect 是从主线程(也是 GUI 线程)调用的。
#include "NetManager.h"
NetManager::NetManager(NetManagerListener& listener) : listener(listener),
connectDeadline(io),
socket1(io),
socket2(io),
socket3(io),
asioThread(&NetManager::handleAsioOperations, this){
}
NetManager::~NetManager() {
running = false;
io.stop();
asioThread.join();
}
void NetManager::connect(){
connectCounter = 0;
hasHandledConnectError = false;
socket1.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_1),
boost::bind(&NetManager::handleConnect, this, _1));
socket2.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_2),
boost::bind(&NetManager::handleConnect, this, _1));
socket3.async_connect(
tcp::endpoint(boost::asio::ip::address::from_string(IP_STRING), PORT_3),
boost::bind(&NetManager::handleConnect, this, _1));
connectDeadline.expires_from_now(boost::posix_time::seconds(CONNECT_TIMEOUT));
connectDeadline.async_wait(boost::bind(&NetManager::handleConnectTimeout, this, _1));
}
void NetManager::disconnect(){
//NOTE: Close also cancels incomplete async operations
socket1.close();
socket2.close();
socket3.close();
}
////////////////////////////////////////////////////////////////////////
/// ASIO Handlers
////////////////////////////////////////////////////////////////////////
void NetManager::handleAsioOperations(){
while(running){
io.run(); // Run any async operations
}
}
void NetManager::handleConnect(const boost::system::error_code &ec){
// When connections are canceled the handler is called with operation_aborted. No need to respond to that.
if(ec && ec != boost::asio::error::operation_aborted && !hasHandledConnectError){
hasHandledConnectError = true; // Likely to be 3 repeated errors. Make sure to only handle the first one
cerr << "Connect Failed: " << ec.message() << endl;
connectDeadline.cancel(); // Don't fire the timeout
disconnect(); // Disconnect any already connected sockets
connectedToRobot = false;
listener.onConnect(false);
}else if (!ec){
connectCounter++;
}
if(connectCounter == 3){
cout << "Successful connect" << endl;
connectDeadline.cancel(); // Don't fire the timeout
connectedToRobot = true;
listener.onConnect(true);
}
}
void NetManager::handleConnectTimeout(const boost::system::error_code &ec){
if(ec != boost::asio::error::operation_aborted){
cerr << "Connect timed out." << endl;
disconnect(); // Disconnect any already connected sockets
connectedToRobot = false;
listener.onConnect(false);
}
}
编辑:
令人困惑的是,这在 Unix 操作系统上运行良好:
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <iostream>
#include <thread>
using namespace boost::asio;
using namespace boost::asio::ip;
int main(){
io_service io;
deadline_timer timer1(io);
tcp::socket sock(io);
timer1.expires_from_now(boost::posix_time::seconds(3));
sock.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8090), [](const boost::system::error_code &ec){
std::cout << "SocketError: " << ec.message() << std::endl;
});
timer1.async_wait([&](const boost::system::error_code &ec){
std::cout << "First timer" << std::endl;
sock.close();
});
std::thread worker([&](){
while(true){
io.run();
}
});
worker.detach();
while(true){} // Simulate the unavailable main (GUI) thread
}
输出:
First timer
SocketError: Operation canceled
好的,我在 Ubuntu 和 Windows 上测试了许多不同的场景后找到了答案。事实证明,如果调用 io_service.run
的线程是在异步任务(async_wait
、async_connect
)创建之前创建的,则使用线程连续调用 io_service::run
在 Unix 操作系统上不起作用开始了。如果工作线程是在调用 async_connect
和 async_wait
之前创建的,我发布的第二个示例会中断(在 unix 上不是 windows)。我刚刚修改了我的 NetManger class 以删除始终 运行 线程。
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <thread>
using namespace boost::asio;
using namespace boost::asio::ip;
class NetManager{
private:
io_service io;
tcp::socket socket1, socket2, socket3;
deadline_timer connectDeadline;
int connectCounter = 0;
void handleConnect(const boost::system::error_code &ec){
if(ec){
std::cout << "Connect error: " << ec.message() << std::endl;
connectDeadline.cancel();
disconnect();
}else
connectCounter++;
if(connectCounter == 3)
std::cout << "Connected" << std::endl;
}
void handleConnectTimeout(const boost::system::error_code &ec){
std::cout << "Timeout fired." << std::endl;
disconnect();
}
public:
void connect(){
connectCounter = 0;
connectDeadline.expires_from_now(boost::posix_time::seconds(5));
socket1.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8090), boost::bind(&NetManager::handleConnect, this, _1));
socket2.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8091), boost::bind(&NetManager::handleConnect, this, _1));
socket3.async_connect(tcp::endpoint(boost::asio::ip::address::from_string("10.50.30.1"), 8092), boost::bind(&NetManager::handleConnect, this, _1));
connectDeadline.async_wait(boost::bind(&NetManager::handleConnectTimeout, this, _1));
std::thread([&]{io.run();}).detach(); // Run the async operations on a separate thread
}
void disconnect(){
socket1.close();
socket2.close();
socket3.close();
}
NetManager(): connectDeadline(io),
socket1(io),
socket2(io),
socket3(io){
}
~NetManager(){
io.stop();
}
};
int main(){
NetManager manager;
manager.connect();
std::cout << "Trying to connect..." << std::endl;
while(true); // Simulate the busy main (GUI) thread.
}
boost::io_service::run
如果没有工作则停止。您不应该像这样在循环中调用它,因为正如文档所述:
A normal exit from the run() function implies that the io_service object is stopped (the stopped() function returns true). Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately unless there is a prior call to reset().
在第一个示例中调用 connect() 时没有显示,我假设不久之后,所以它是否有效取决于线程启动的速度和 run()
调用已执行。
您可以使用 boost::io_service::work
来规避此行为,这将防止 io_service
运行 停止工作。