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

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

Related

boost asio open socket without using deprecated methods

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.

how to determine whether boost async_accept is listening on a port or not

I am unable to get this example working
http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html
I have changed the port 13 to 1163 so that I don't need to be a root user to start listening.
And I am running the io_service in separate thread.
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.detach();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
string wait;
cin >> wait;
return 0;
}
When testing the above server with http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/tutorial/tutdaytime1/src.html client, it says connection refused.
netstat --listen didn't show any open ports on 1163
I couldn't figure out how to use boost::asio::async_result<typename Handler> I am confused on Handler.
Working modification
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.detach();
string wait;
cin >> wait;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
If the wait is inside the try block, the code working!
If asio can't listen on the port the creation or binding of the acceptor will already fail. In your case you are creating the acceptor with acceptor_(io_service, tcp::endpoint(tcp::v4(), 13)), which is this overload: http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/basic_socket_acceptor/basic_socket_acceptor/overload3.html
This will directly try to bind the socket and throw an exception if that fails. The alternative (which is described on the bottom of this page) is to create the socket without an assigned endpoint and call open and bind on it, where bind will fail with an error (e.g. if the socket is already in use). In any case you won't need to wait for accept/async_accept to see an error.
I guess your issue is in the client program, which still tries to connect to port 13. Have you changed it to use port 1163? In the example code the port is not directly written, but a well-known service name is used here: tcp::resolver::query query(argv[1], "daytime");. The "daytime" reference will tell the resolver to use port 13.
Update: Now that that I see the actual code with the thread it's a totally different error:
If the wait is not inside the try block the asio io_service and tcp_server will go out of scope nearly immediatly, which means their destructors are called. This will stop all communication. Even worse, the detached thread now operates on some dangling pointers. As a general rule asio objects (the io_service eventloop, sockets, etc.) should all live in the the thread that uses them. The lifetime of the io_service should be tied to or shorter than the lifetime of a thread. The lifetime of the sockets should be shorter than that of the eventloop (io_service) that runs them. Other usage scenarios might be possible by using shared_ptr's or by putting a lots of thoughts into the design, but that's not what I would recommend.

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.