为什么在Unix上无法使用boost-offline_timer用作async_connect的超时?

时间:2018-06-20 23:22:34

标签: c++ unix boost asio deadline-timer

我有一个程序,使用deadline_timer作为连接超时,使用boost asio异步连接到3个TCP套接字。在Windows上,一切正常。 5秒后连接超时。但是,在Unix(WSL,Linux Mint VM,macOS的Ubuntu)上,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

2 个答案:

答案 0 :(得分:0)

好吧,在Ubuntu和Windows上测试了许多不同的场景之后,我找到了答案。事实证明,如果在异步任务(io_service::runio_service.run)被创建之前创建了调用async_wait的线程,则在Unix OS上使用线程连续调用async_connect是行不通的。开始。如果发布工作线程是在调用async_connectasync_wait之前创建的,那么我发布的第二个示例会中断(在unix而非Windows上)。我刚刚修改了NetManger类以删除始终运行的线程。

#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.
}

答案 1 :(得分:0)

boost::io_service::run如果没有工作,则停止。您不应在这样的循环中调用它,因为作为文档状态:

  

从run()函数正常退出意味着io_service   对象已停止(stopped()函数返回true)。后续的   调用run(),run_one(),poll()或poll_one()将返回   立即进行,除非事先调用过reset()。

在第一个示例中,当您调用connect()时,您不会显示,我认为它不会很快,因此它起作用与否的事实归结为线程启动的速度和{{1} }调用已执行。

您可以使用run()来规避此行为,这将防止boost::io_service::work耗尽工作。