boost asio open socket without using deprecated methods - c++

Modern boost asio has deprecated the code used in every example I can find. The examples suggest code like this:
const char * host = "www.google.com";
const char * port = "http";
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(host, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end) {
socket.close();
socket.connect(*endpoint_iterator++, error);
}
However, both the iterator and the query are marked deprecated. My google searches to find an example of the new and improved way has come up with nothing so far.
Does anyone either have a modern example to point me to, or can just provide a snippet of how to get that socket open using modern boost asio? I don't like using deprecated methods.

resolve which takes query is deprecated, you are right here. But resolver has these overloads
results_type resolve(
string_view host,
string_view service);
// and N-th other overloads
where host is IP, and service is port. What is results_type? It is sequence of endpoints to which you can try to establish the connection.
Your code can be translated to something like this:
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
auto endpoints = resolver.resolve("www.google.com","http"); // [1]
boost::asio::ip::tcp::socket socket(io_service);
boost::system::error_code ec;
auto finalEndpoint = boost::asio::connect(socket,endpoints.begin(),endpoints.end(),ec); // [2]
if (ec) { /*ups, error here */} else { /*socket is connected, read/send data/ */ }
in [1] you are passing host/port of service you want to connect to.
in [2] you call connect (since 1.64 boost version method) which iterates over all endpoints and establish connection. Then you don't need to write your own loop.

Related

boost::asio, Creating a std::vector <boost::asio::ip::tcp::iostream>?

I need to extend an boost::asio::ip::tcp::iostream application to be able to connect to multiple clients. For this, i am trying to create a:
std::vector<boost::asio::ip::tcp::iostream>
and add iostreams, like this:
std::vector<boost::asio::ip::tcp::iostream> streams;
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint
= boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 4444);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
boost::asio::ip::tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
streams.emplace_back(std::move(stream)); // gives error
the above code gives me an Attempted to reference a deleted function error.
I have tried push_back, and emplace_back as well. Is there a way to use a std::vector<boost::asio::ip::tcp::iostream>?

Handling socket boost asio tcp ip - C++ socket programming

I am successfully establishing connection, sending and receiving messages using the following code. What I want to do is somehow to return that already established connection. I assume that I need to return the socket.
Before writing this topic I read a few related topics - in some of them it was mentioned that returning a socket is not a good idea. Usage of shared is suggested here. Passing around boost::asio::ip::tcp::socket
Unfortunately I am not familiar with this type of pointers and their usage. Could you help me with fixing that problem.
try {
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(server, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
my_socket = new tcp::socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::connect(*my_socket, endpoint_iterator);
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
if you have c++11 or higher, ignore all that nonsense about shared pointers and return the socket. As of c++11 asio io objects support move construction and move assignment. An asio socket is extremely lighweight - the structure contains two pointers, nothing more. – Richard Hodges

How do I convert a host name in to a boost address or endpoint?

I'm using a C++ Redis Library on top of Boost. (https://github.com/nekipelov/redisclient)
To connect, I have to give it either a single tcp endpoint:
boost::asio::ip::tcp::endpoint
Or an address + port
boost::asio::ip::address, unsigned short
Currently, I started with:
boost::asio::ip::address address = boost::asio::ip::address::from_string(someIPVariable);
and passed that along with the port, it worked fine and connected. However, I now need to do it by hostname instead of IP. If I simply put the host name in to the line above, it throws an exception as I think it expects an IP address.
I'm used to specifying connections as just ("IP OR Hostname", port) so I'm a little unsure what's required here. I checked the constructors for both to see if any could convert a host name + port to what was required, but I can't find anything.
You need to use a tcp::resolver to do name resolution (i.e. DNS lookup):
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("example.com", "80");
boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
Dereferencing the iterator gives you a resolver entry that has a tcp::endpoint:
boost::asio::ip::tcp::endpoint endpoint = iter->endpoint();
Old thread but for anyone interested...
Asynchronous version:
boost::asio::io_service io_service;
boost::asio::io_service::strand io_strand(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
resolver.async_resolve({"host", "port"},
io_strand.wrap([this](const boost::system::error_code& ec,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
if (!ec)
{
std::for_each(endpoint_iterator, {}, [this](auto& it)
{
std::cout << it.endpoint().address() << std::endl;
});
}
else
{
//ec.message()?
}
}));
Note(1): we can have multiple results from the resolve function due to systems having multiple network interfaces/adapters. This is why we should iterate through the results and check "whatever" suits your needs. E.g. check if endpoint.address().is_v4();.
Note(2): don't forget to run the I/O service: io_service.run();.

Opening a boost socket with zero padding IP string

I'm using boost 1.53 and have the following code for opening a socket :
void openConnection(std::string ip, std::string port)
{
tcp::resolver resolver_( m_ioService );
tcp::resolver::query query(boost::asio::ip::tcp::v4(), ip, port);
tcp::resolver::iterator iterator = resolver_.resolve(query);
boost::shared_ptr<tcp::socket> socket_(new tcp::socket(m_ioService));
boost::asio::connect(*socket_, iterator);
m_tcpSocket = socket_;
}
If I use ip = "192.168.2.50" I'm fine but if I use "192.168.002.050" I'm failed opening a connection.
Is there any way to tune tcp::resolver::query object for such kind of strings ?
If 192.168.002.50 does work I'd say it's a failed attempt at octal decoding (that shouldn't happen).
Otherwise it seems like a limitation in boost::asio::ip::address_v::from_string. This will - no doubt - have reasonable grounds (I don't have the RFCs memorized).
You can always just manually parse, and use the constructor that takes the octets:
address_v4(address_v4::bytes_type {192,168,2,50});
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/ip__address_v4.html

Passing around boost::asio::ip::tcp::socket

Alright, this is my current code snippet:
namespace bai = boost::asio::ip;
bai::tcp::socket tcp_connect(std::string hostname, std::string port) {
try {
boost::asio::io_service io_service;
bai::tcp::resolver resolver(io_service);
// we now try to get a list of endpoints to the server
bai::tcp::resolver::query query(hostname, port);
bai::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
bai::tcp::resolver::iterator end;
// looking for a successful endpoint connection
bai::tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end) {
socket.close();
socket.connect(*endpoint_iterator++ , error);
}
if (error) throw boost::system::system_error(error);
return socket;
} catch (std::exception &ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
}
Which should return a boost::asio::ip::tcp::socket connected to hostname on port. However I get presented with a shitload of incomprehensible boost::noncopyable errors. But my question is, how should I pass around these sockets then? What's wrong with this?
socket can't be copied. Use a boost::shared_ptr<bai::tcp::socket> instead. If you could copy a socket you'd have all sorts of funny issues if you ended up with two socket instances representing the same underlying OS socket - so it makes sense that copying (and therefore return by value, pass by value) is not allowed.
The code:
return socket;
attempts to make a copy of socket to return, and then destroy the original socket when the function exits. Unfortunately, sockets cannot be copied (they manage an underlying operating system resource that must be closed, so the system must ensure only one reference to that resource exists, otherwise things would go badly wrong when the first copy went out of scope).
As suggested in the other answer, you could use a pointer to return an object created on the heap (which should be managed either with a shared_ptr, or more efficiently if you are only using it in a single place a unique_ptr), or if you are using C++11 you could use the move constructor for the return value:
return std::move (socket);
This would avoid the necessity to use heap allocation and pointers, so is probably the preferable solution.