Rickser
Rickser

Reputation: 45

Is it possible to add multiple server adresses? (Asio, Client->Server)

I wrote a small C++ asio program and now I want to add a backup server to the server "list" of the client. The client is trying to connect to the server via the asio resolver, but if the server is offline, the client should automatically connect to the "backup server". So can I add multiple addresses to the resolver or do I need to write the whole "try to connect -> fail -> try to connect to next server" function?

Upvotes: 1

Views: 476

Answers (2)

sehe
sehe

Reputation: 392931

Though the other answer correctly asserts the thing you're looking for exactly doesn't exist, you can very easily build it:

struct multi_iterator : tcp::resolver::iterator {
    using base = typename tcp::resolver::iterator;

    multi_iterator() = default;
    template <typename T>
    explicit multi_iterator(T&& v) : base(std::forward<T>(v)) {}

    using base::values_;
    void append(tcp::resolver::results_type const& r) {
        if (!values_) values_.reset(new typename base::values_type);
        values_->insert(values_->end(), r.begin(), r.end());
    }
};

Now you can combine the results of many queries:

multi_iterator it;
std::vector<tcp::resolver::query> queries {
    { "localhost",  "8080"  },
    { "localhost",  "8081"  },
    { "google.com", "https" },
    { "localhost",  "6767"  },
};
for (auto query : queries)
    it.append(tcp::resolver(io).resolve(query));

Using boost::asio::connect or boost::asio::async_connect to do the endpoint iteration:

tcp::socket sock(io);
auto ep = *boost::asio::connect(sock, it);
std::cout << "Connected to " << ep.endpoint() << " (for " << ep.host_name() << ":" << ep.service_name() << ")\n";

On my system this prints

Connected to 172.217.19.206:443 (for google.com:https)

because I had nothing listening on ports 8080 or 8081 locally. After opening one of those with netcat -l -p 8081:

Connected to 127.0.0.1:8081 (for localhost:8081)

Full Demo

Also including a more generalized basic_multi_iterator<Protocol>:

#include <boost/asio.hpp>
#include <iostream>

template <typename Proto>
struct basic_multi_iterator : boost::asio::ip::basic_resolver<Proto>::iterator {
    using resolver = typename boost::asio::ip::basic_resolver<Proto>;
    using base = typename resolver::iterator;

    basic_multi_iterator() = default;
    template <typename T> explicit basic_multi_iterator(T&& v)
        : base(std::forward<T>(v))
    {}

    using base::values_;
    void append(typename resolver::results_type const& r) {
        if (!values_) 
            values_.reset(new typename base::values_type);
        values_->insert(values_->end(), r.begin(), r.end());
    }
};

using boost::asio::ip::tcp;
using multi_iterator = basic_multi_iterator<tcp>;

int main() {
    boost::asio::io_context io;

    multi_iterator it;
    std::vector<tcp::resolver::query> queries {
        { "localhost",  "8080"  },
        { "localhost",  "8081"  },
        { "google.com", "https" },
        { "localhost",  "6767"  },
    };
    for (auto query : queries)
        it.append(tcp::resolver(io).resolve(query));

    try {
        tcp::socket sock(io);
        auto ep = *boost::asio::connect(sock, it);

        std::cout << "Connected to " << ep.endpoint() << " (for " << ep.host_name() << ":" << ep.service_name() << ")\n";
    } catch (boost::system::system_error const& e) {
        std::cout << e.what() << " - " << e.code().message() << "\n";
    }
}

Upvotes: 4

Litty
Litty

Reputation: 1876

Asio does not provide this kind of "skip and try another server" functionality out-of-the-box. You will need to write that code yourself.

Upvotes: 0

Related Questions