用 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 直到建立连接。
我正在尝试编写一种非常优雅的方法来使用 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 直到建立连接。