0

我有一个程序使用 boost asio 异步连接到 3 个 TCP 套接字,使用 adeadline_timer作为连接超时。在 Windows 上,一切都按预期工作。5 秒后连接超时。但是,在 Unix(WSL 上的 Ubuntu、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
4

2 回答 2

0

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

#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.
}
于 2018-06-22T15:30:39.500 回答
0

boost::io_service::run如果没有工作就停止。您不应该在这样的循环中调用它,因为正如文档所述:

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

在第一个示例中调用 connect() 时没有显示,我认为它不会在不久之后显示,因此它是否工作的事实归结为线程启动和run()调用执行的速度。

您可以通过使用 which 来规避这种行为,boost::io_service::work这将防止io_service工作用尽。

于 2018-06-22T15:53:30.257 回答