A client (at last!)

111

The key difference between clients and servers is this: a client connects to something on its own while a server waits for an incoming connection instead. After the connection is established, both of them use the same classes and functions to work with this connection. However, usually there are more distinctions between real life clients and servers:

Servers are designed to serve many connections from different clients simultaneously. Clients usually serve one connection. A client may establish multiple connections to the same server for better performance, or connect to multiple different servers to do several different tasks in parallel though.

A server additionally may act like a client. For example, a web server usually connects to a database server while processing user's request. At this point a web server is a database client at the same time.

A client is usually designed to interact with a human (it has a user interface, etc), while a server is usually a sort of standalone service working on its own.

So, since this time we're talking about clients, let's see how to connect to something on our own. To do that we need to call socket::connect or socket::async_connect function and pass an instance of endpoint class as a parameter. The same endpoint which we learned about in the “Learning further” lesson.

To construct an endpoint object we need to know its both IP address and port. Port is just a number. IP address is an instance of boost::asio::ip::address class. If you know the value of the IP address then you can construct it from a string:

boost::asio::ip::address address = boost::asio::ip::make_address("127.0.0.1");

Now, having an address object, we can construct the endpoint and try to connect to it:

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

namespace io = boost::asio;
namespace ip = io::ip;
using tcp = io::ip::tcp;
using error_code = boost::system::error_code;

int main()
{
    io::io_context io_context;
    tcp::socket socket(io_context);
    ip::address address = ip::make_address("127.0.0.1");
    tcp::endpoint endpoint(address, 80);

    error_code error;
    socket.connect(endpoint, error);

    if(!error)
    {
        std::cout << "The connection has been established!";
    }
    else
    {
        std::cerr << "Something went wrong :(";
    }

    return 0;
}

This will success if there is some application waiting (or listening) for an incoming connection on 127.0.0.1:80 and there are no barrier (like a firewall or an ill-formed routing table) for our client to connect to that application.

As you may know, there are two IP address standards: IPv4 and IPv6. There are special classes and functions for both of these standards: ip::address_v4, ip::make_address_v4, ip::address_v6 and ip::make_address_v6. Also there is a generic class and function which supports both of the standards and which I used in the example above: ip::address and ip::make_address. You should use them when you don't really care which standard you want to deal with. So, you can pass IPv6 string representation into make_address function and it will work just fine:

ip::address address = ip::make_address("2001:4860:4860::8888");

When the connection is established, we can use the same read and write functions which we learned about when we discussed how to write servers.

Usually in real life we don't use IP addresses directly. Instead, we use hostnames, or domain names, like google.com. To connect to a server by its hostname, first thing we need to do is find out what IP address lies behind that hostname. And this is the subject of the next lesson.

Lesson 14
Share this page:

Learning plan

We've dealt with a single-threaded environment so far; now it's time to go multithreading
A special execution model with a custom load balancer
14. Timers
Working with asynchronous timers within io_context polling loop
15. A client (at last!)
What's the difference between a client and a server, and what do they have in common
Resolving hostnames into IP addresses before connect
Writing a very simple client application in C++ with Boost.Asio
How to deal with completion handlers manually to combine Boost.Asio with other APIs