用 boost::asio 重新连接循环的优雅方式?

Elegant way of reconnecting loop with boost::asio?

我正在尝试编写一种非常优雅的方法来使用 boost async_connect(...) 处理重新连接循环。问题是,我不知道如何优雅地解决以下问题:

我有一个 TCP 客户端应该尝试异步连接到服务器,如果由于服务器离线或发生任何其他错误而导致连接失败,请等待给定的时间并尝试重新连接。这里有很多事情需要考虑:

一个非常基本的客户端实例化如下:

tcpclient::tcpclient(std::string host, int port) : _endpoint(boost::asio::ip::address::from_string(host), port), _socket(_ios) {
    logger::log_info("Initiating client ...");
}

尝试连接到服务器:

void tcpclient::start() {

    bool is_connected = false;

    while (!is_connected) {
        _socket.async_connect(_endpoint, connect_handler);
        _ios.run();
    }

    // read write data (?)

}

负责人:

void tcpclient::connect_handler(const boost::system::error_code &error) {

    if(error){
        // trigger disconnect (?)
        logger::log_error(error.message());
        return;
    }

    // Connection is established at this point
    // Update timer state and start authentication on server ?

    logger::log_info("Connected?");

}

每次连接失败(或掉线)时如何正确地开始重新连接?由于处理程序是静态的,我无法修改指示连接状态的 class 属性?我想避免使用 hacky 全局变量解决方法。 我该如何正确解决这个问题?

我的尝试是这样的:

tcpclient.h

enum ConnectionStatus{
    NOT_CONNECTED,
    CONNECTED
};


class tcpclient {

public:

    tcpclient(std::string host, int port);

    void start();

private:

    ConnectionStatus _status = NOT_CONNECTED;

    void connect_handler(const boost::system::error_code& error);

    boost::asio::io_service _ios;
    boost::asio::ip::tcp::endpoint _endpoint;
    boost::asio::ip::tcp::socket _socket;

};

tcpclient.cpp

#include "tcpclient.h"
#include <boost/chrono.hpp>
#include "../utils/logger.h"

tcpclient::tcpclient(std::string host, int port) : _endpoint(boost::asio::ip::address::from_string(host), port),
                                                   _socket(_ios) {
    logger::log_info("Initiating client ...");
    logger::log_info("Server endpoint: " + _endpoint.address().to_string());
}


void tcpclient::connect_handler(const boost::system::error_code &error) {

    if(!error){
        _status = CONNECTED;
        logger::log_info("Connected.");
    }
    else{
        _status = NOT_CONNECTED;
        logger::log_info("Failed to connect");
        _socket.close();
    }

}

void tcpclient::start() {

    while (_status == NOT_CONNECTED) {
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        _socket.close();
        _socket.async_connect(_endpoint, std::bind(&tcpclient::connect_handler, this, std::placeholders::_1));
        _ios.run();
    }
}

问题是重新连接工作不正常,应用程序似乎由于某种原因冻结了?除此之外,一旦建立连接然后断开连接,重新连接似乎也有问题(例如,由于服务器crashing/closing)。

std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 将冻结程序 2 秒。您可以在这里做的是在连接尝试失败时启动异步计时器:

::boost::asio::steady_timer m_timer{_ios, boost::asio::chrono::seconds{2}};

void tcpclient::connect_handler(const boost::system::error_code &error)
{
    if(!error)
    { 
        _status = CONNECTED;
        logger::log_info("Connected.");
    }
    else
    {
        _status = NOT_CONNECTED;
        logger::log_info("Failed to connect");
       _socket.close();
       m_timer.expires_from_now(boost::asio::chrono::seconds{2});
       m_timer.async_wait(std::bind(&tcpclient::on_ready_to_reconnect, this, std::placeholders::_1));
    }
}

void tcpclient::on_ready_to_reconnect(const boost::system::error_code &error)
{
    try_connect();
}

void tcpclient::try_connect()
{
    m_socket.async_connect(_endpoint, std::bind(&tcpclient::connect_handler, this, std::placeholders::_1));
}

void tcpclient::start()
{
    try_connect();
    _ios.run();
}

也不需要 while (_status == NOT_CONNECTED) 循环,因为 io 服务会很忙,而 _ios.run(); 不会 return 直到建立连接。