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

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>?

Related

Get io_context reference from a socket in Boost 1.73+ Asio

How can I get boost::asio::io_context reference from a socket? Previously there were socket::get_io_service and then socket::get_io_context member functions, however now they both are deprecated. I've found the only way to do this in Boost 1.73+:
boost::asio::ip::tcp::socket socket(...);
// ...
boost::asio::io_context& io_context = static_cast<boost::asio::io_context&>(socket.get_executor().context());
This works, however looks ugly and dangerous. Is there a better way?
You would probably want to get the executor, which might be something other than the io_context.
There's a get_executor() call to do it directly:
boost::asio::io_context io;
boost::asio::ip::tcp::socket s(io);
auto ex = s.get_executor();
The executor will allow you to do most things you were probably using the io_context for.
UPDATE
To the comment, I do NOT recommend relying on the exact target of the executor you get passed in via any service object, but you can force your hand if you really don't want to update your design right now:
Live On Coliru
#include <boost/asio.hpp>
int main() {
boost::asio::io_context io;
boost::asio::ip::tcp::socket s(io);
auto ex = s.get_executor();
auto* c = ex.target<boost::asio::io_context>();
boost::asio::ip::tcp::socket more_sockets(*c);
assert(c == &io);
}
When compositing async operations, you can derive an executor from a handler using boost::asio::get_associated_executor()

boost::asio crash when using a member acceptor instead of new one

I am trying to put the acceptor, socket and endpoint as members into my class but ran into crashes. Must the socket be a shared_ptr like in this Question or why does it not work?
When I'm trying to setup a acceptor on a server like this:
tcp::endpoint ep(boost::asio::ip::address::from_string(localIpAddress), portNumber);
tcp::acceptor a(io_service);
tcp::socket s(io_service);
a.open(ep.protocol());
a.bind(ep);
a.listen(MAX_CONNECTIONS);
a.async_accept(s, boost::bind(&WifiConnector::onAccept, this, boost::asio::placeholders::error));
it runs without crashing during execution, but when I try to use a socket/acceptor/endpoint that are member of my WifiConnector class it crashes.
m_acceptor.open(localEndpoint.protocol()); // it crashes in this line already
m_acceptor.bind(localEndpoint);
m_acceptor.listen(MAX_CONNECTIONS);
m_acceptor.async_accept(socket, boost::bind(&WifiConnector::onAccept, this, boost::asio::placeholders::error));
declaration in WifiConnector.hpp:
private:
tcp::socket m_socket;
tcp::acceptor m_acceptor;
tcp::endpoint m_localEndpoint;
initialization at class constructor:
WifiConnector::WifiConnector() :
io_service(),
m_socket(io_service),
m_acceptor(io_service)
{
m_localIpAddress = "192.168.137.1";
m_portNumber = 30000;
m_localEndpoint = tcp::endpoint(boost::asio::ip::address::from_string(m_localIpAddress), m_portNumber);
}
when it crashes, I get the following exeption:
boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >
private:
tcp::socket m_socket;
tcp::acceptor m_acceptor;
tcp::endpoint m_localEndpoint;
This will not work. You are constructing using the default constructors, which is not what you want. For one thing you want to construct using the io_service used by everything else.
Make the attributes pointers, and construct them using new when you have the io_service.

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();.

Compilation failure when using std::shared_ptr instead of boost::shared_ptr

The code below successfully sends an async message to the given endpoint.
// message is a boost::shared_ptr<std::string>
// open a UDP socket
boost::asio::ip::udp::socket socket(ioService);
socket.open(boost::asio::ip::udp::v4());
// create the remote endpoint
boost::asio::ip::udp::endpoint remoteEndpoint(boost::asio::ip::address_v4::from_string(address), port);
// asynchronously send a datagram to the remote endpoint
socket.async_send_to(boost::asio::buffer(*message),
remoteEndpoint,
boost::bind(&MyClass::handler,
this,
message,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
socket.close();
However, if I change the type of message to a std::shared_ptr<std::string> rather than a boost::shared_ptr<std::string> then the call to async_send_to doesn't compile.
The error is:
boost/boost/bind/bind.hpp:457:9: No matching function for call to object of type 'boost::_mfi::mf3<void, MyClass, const boost::shared_ptr<std::__1::basic_string<char> > &, const boost::system::error_code &, unsigned long>'.
Can someone explain what is wrong? Is it possibly because I'm using boost::bind?
Looks like problem is, that you handler function receives boost::shared_ptr, not std::shared_ptr and boost::shared_ptr is not constructible from std::shared_ptr.

create socket after io_service run

In all examples of using boost, usually people do the following
boost::asio::io_service io_service;
tcp::socket s1(io_service);
tcp::socket s2(io_service);
io_service.run();
But i am writing class that already has running in thread io_service and it has to create sockets with this io_service. And there is my question. How to make it thread safety?
class MySocket
{
private:
boost::asio::io_service* ioService;
tcp::socket* socket;
public:
MySocket(boost::asio::io_service* nioService,
tcp::resolver::iterator endpoint_iterator):
ioService(nioService)
{
socket = new tcp::socket(*ioService);
}
~MySocket();
};
SocketHandler handler;
handler.run(); //run io_service in thread
MySocket* s1 = handler.createSocket("localhost", "80");
//do something
MySocket* s2 = handler.createSocket("localhost", "81");
//dododo
handler.destroySocket(s1);
handler.destroySocket(s2);
You can create new sockets at any time with boost::asio.
io_service::run() blocks until working queue is empty. If it there is no work in the queue - the function returns immediately. That's why people usually add work to it (create timers, bind sockets, etc) prior to io_service::run().
BTW: I don't recommend doing this way:
MySocket* s1 = handler.createSocket("localhost", "80");
...
handler.destroySocket(s1);
use RAII-objects (smart pointers) instead.