Exemplos

Cliente NTP

Nota: Nos formatos da data e o timestamp, a base era 0, resultando inicialmente no seguinte horário: 0h de 1 de janeiro de 1900 UTC, quando todos os bits são zero.

Referência: RFC 5902

Para exibir o horário atual, precisará alterar o timestamp!

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

namespace asio = boost::asio;
using asio::ip::udp;

int main(int argc, char *argv[]) {
  try {
    if (argc != 2) {
      std::cerr << "Usage: ntp_client <host>" << std::endl;
      return 1;
    }

    asio::io_context io_context;

    udp::resolver resolver(io_context);
    udp::endpoint receiver_endpoint =
        *resolver.resolve(udp::v4(), argv[1], "123").begin();

    udp::socket socket(io_context);
    socket.open(udp::v4());

    std::array<char, 48> send_buf = {0x1b, 0, 0, 0, 0, 0, 0, 0, 0};
    socket.send_to(asio::buffer(send_buf), receiver_endpoint);

    std::array<char, 48> recv_buf;
    udp::endpoint sender_endpoint;
    size_t len = socket.receive_from(asio::buffer(recv_buf), sender_endpoint);

    std::cout << "received " << len << " bytes from " << sender_endpoint
              << std::endl;

    // Extrair o NTP timestamp da resposta (do servidor)
    unsigned long long int ntp_timestamp =
      (unsigned long long int)(recv_buf[40]) << 24 |
      (unsigned long long int)(recv_buf[41]) << 16 |
      (unsigned long long int)(recv_buf[42]) << 8 |
      (unsigned long long int)(recv_buf[43]);

   // Converter o timestamp para std::chrono::system_clock::time_point
    std::chrono::system_clock::time_point time_point =
        std::chrono::system_clock::time_point(
            std::chrono::seconds(ntp_timestamp - 2208988800ull));
                              // ntp_timestamp - unix_timestamp

    // Converter o time_point para std::time_t e exibir na tela
    std::time_t time = std::chrono::system_clock::to_time_t(time_point);
    std::cout << std::ctime(&time) << std::endl;

  } catch (std::exception &e) {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

DNS resolver (ShowMeIP)

#include <iostream>
#include <string>
#include <asio.hpp>

int main(int argc, char* argv[])
{
    // Verificar o número de argumentos
    if (argc != 2) {
        std::cerr << "Usage: showip hostname" << std::endl;
        return 1;
    }

    // Descobrir o endereço IP por baixo do link mencionado no argv[1]
    asio::io_context io_context;
    asio::ip::tcp::resolver resolver(io_context);
    asio::ip::tcp::resolver::query query(argv[1], "");
    auto results = resolver.resolve(query);

    // Iterar todos os IPs detectados e exibi-los na tela.
    std::cout << "IP addresses for " << argv[1] << ":" << std::endl << std::endl;
    for (auto result : results) {
        std::cout << "  " << result.endpoint().address().to_string() << std::endl;
    }

    return 0;
}

QuickSort com Corrotinas

Referência: Zap/Cpp benchmark - An asynchronous runtime with a focus on performance and resource efficiency.

#include <algorithm>
#include <asio.hpp>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>

using namespace std::chrono;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;

awaitable<void> quickSort(asio::io_context &ctx,
                          std::vector<int>::iterator begin,
                          std::vector<int>::iterator end) {
  if (std::distance(begin, end) <= 32) {
    // Use std::sort for small inputs
    std::sort(begin, end);
  } else {
    auto pivot = begin + std::distance(begin, end) - 1;
    auto i = std::partition(begin, pivot, [=](int x) { return x <= *pivot; });
    std::swap(*i, *pivot);

    co_await quickSort(ctx, begin, i);
    co_await quickSort(ctx, i + 1, end);
  }
  co_return;
}

void shuffle(std::vector<int> &arr) {
  std::mt19937 rng(std::random_device{}());
  std::shuffle(std::begin(arr), std::end(arr), rng);
}

int main() {
  std::vector<int> arr(10'000'000);

  std::cout << "filling" << std::endl;
  std::iota(std::begin(arr), std::end(arr), 0);

  std::cout << "shuffling" << std::endl;
  shuffle(arr);

  std::cout << "running" << std::endl;

  const int num_threads = std::thread::hardware_concurrency();
  asio::io_context ctx{num_threads};
  const auto start = high_resolution_clock::now();

  co_spawn(
      ctx,
      [&]() -> awaitable<void> {
        co_await quickSort(ctx, std::begin(arr), std::end(arr));
      },
      detached);

  // Run the io_context to process the posted tasks
  ctx.run();

  const auto elapsed =
      duration_cast<milliseconds>(high_resolution_clock::now() - start);
  std::cout << "took " << elapsed.count() << "ms" << std::endl;

  if (!is_sorted(std::begin(arr), std::end(arr))) {
    throw std::runtime_error("array not sorted");
  }
}

Servidor TCP com WolfSSL (base)

Nota: Apenas ilustrativo. Requer aprimoramento complementar!

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <wolfssl/ssl.h>

namespace asio = boost::asio;
using asio::ip::tcp;

class wolfSSL_context
{
public:
  wolfSSL_context(asio::io_context& io_context,
                  asio::ssl::context::method method)
    : context_(io_context, method)
  {
    context_.set_options(
      asio::ssl::context::default_workarounds
      | asio::ssl::context::no_sslv2
      | asio::ssl::context::single_dh_use);

    // Utilizar os certificados.
    context_.use_certificate_chain_file("server.crt");
    context_.use_private_key_file("server.key", asio::ssl::context::pem);
    context_.use_tmp_dh_file("dh2048.pem");
  }

  asio::ssl::context& context()
  {
    return context_;
  }

private:
  asio::ssl::context context_;
};

class wolfSSL_stream
  : public asio::ssl::stream<tcp::socket>
{
public:
  wolfSSL_stream(asio::io_context& io_context, wolfSSL_context& context)
    : asio::ssl::stream<tcp::socket>(io_context, context.context())
  {
  }
};

class wolfSSL_server
{
public:
  wolfSSL_server(asio::io_context& io_context,
                 unsigned short port)
    : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
      context_(io_context, asio::ssl::context::tlsv12)
  {
    start_accept();
  }

private:
  void start_accept()
  {
    wolfSSL_stream new_stream(acceptor_.get_io_context(), context_);

    acceptor_.async_accept(new_stream.next_layer(),
                           std::bind(&wolfSSL_server::handle_accept, this,
                                     std::placeholders::_1,
                                     std::move(new_stream)));
  }

  void handle_accept(const asio::error_code& error,
                     wolfSSL_stream stream)
  {
    if (!error)
    {
      stream.handshake(asio::ssl::stream_base::server);

      // Executar o Handshake com SSL/TLS e ler os dados do cliente.
      // ...

      start_accept();
    }
  }

  tcp::acceptor acceptor_;
  wolfSSL_context context_;
};

int main()
{
  try
  {
    asio::io_context io_context;

    wolfSSL_server server(io_context, 443);

    io_context.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

Noise-C com Asio

Cliente:

#include <iostream>
#include <array>
#include <boost/asio.hpp>
#include <noise/protocol.h>
#include <noise/handshake.h>

using boost::asio::ip::tcp;

int main(int argc, char *argv[]) {

    // Declara as chaves públicas estáticas local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_static_public_key;
    local_static_public_key.fill(0x55);
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_static_public_key;
    remote_static_public_key.fill(0xAA);

    // Declara as chaves públicas efêmeras local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_ephemeral_public_key;
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_ephemeral_public_key;

    // Declara as chaves privadas efêmeras local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_ephemeral_private_key;
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_ephemeral_private_key;

    // Declara o estado da negociação de chave
    NoiseHandshakeState *state = 0;

    // Inicializa o estado da negociação de chave
    int result = noise_handshakestate_new_by_name(
        &state, "Noise_NN_25519_ChaChaPoly_BLAKE2s", 0);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error initializing handshake state" << std::endl;
        return 1;
    }

    // Define as chaves públicas estáticas local e remota
    noise_handshakestate_set_local_static_public_key(state, local_static_public_key.data(), local_static_public_key.size());
    noise_handshakestate_set_remote_static_public_key(state, remote_static_public_key.data(), remote_static_public_key.size());

    // Gera o par de chaves efêmeras local
    result = noise_handshakestate_generate_local_keypair(state, local_ephemeral_private_key.data(), local_ephemeral_private_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error generating local ephemeral key pair" << std::endl;
        return 1;
    }
    noise_handshakestate_get_local_public_key(state, local_ephemeral_public_key.data(), local_ephemeral_public_key.size());

    // Cria um objeto boost::asio::io_context
    boost::asio::io_context io_context;

    // Cria um objeto boost::asio::ip::tcp::socket
    tcp::socket socket(io_context);

    // Conecta ao servidor
    boost::asio::connect(socket, tcp::resolver(io_context).resolve({ "localhost", "1234" }));

    // Envia a chave pública efêmera local ao servidor
    boost::asio::write(socket, boost::asio::buffer(local_ephemeral_public_key));

    // Recebe a chave pública efêmera remota do servidor
    boost::asio::read(socket, boost::asio::buffer(remote_ephemeral_public_key));
    noise_handshakestate_set_remote_ephemeral_public_key(state, remote_ephemeral_public_key.data(), remote_ephemeral_public_key.size());

    // Gera a chave privada efêmera remota
    result = noise_handshakestate_generate_remote_key(state, remote_ephemeral_private_key.data(), remote_ephemeral_private_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error generating remote ephemeral private key" << std::endl;
        return 1;
    }

    // Realiza a negociação de chave (handshake)
    size_t message_len;
    std::array<uint8_t, MAX_HANDSHAKE_MESSAGE_LEN> message;
    result = noise_handshakestate_start(state, message.data(), &message_len);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error starting handshake" << std::endl;
        return 1;
    }
    // Envia a mensagem ao servidor
    boost::asio::write(socket, boost::asio::buffer(message, message_len));
    // Recebe a resposta do servidor
    boost::asio::read(socket, boost::asio::buffer(remote_ephemeral_public_key));
    result = noise_handshakestate_write_message(state, remote_ephemeral_public_key.data(), remote_ephemeral_public_key.size(), message.data(), &message_len);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error writing handshake message" << std::endl;
        return 1;
    }
    // Envia a mensagem ao servidor
    boost::asio::write(socket, boost::asio::buffer(message, message_len));
    // Recebe a resposta do servidor
    boost::asio::read(socket, boost::asio::buffer(remote_ephemeral_public_key));
    result = noise_handshakestate_read_message(state, message.data(), message_len, remote_ephemeral_public_key.data(), &remote_ephemeral_public_key.size());
  if (result != NOISE_ERROR_NONE) {
    std::cerr << "Error reading handshake message" << std::endl;
    return 1;
  }

  // Libera o estado do handshake
  noise_handshakestate_free(state);

  // Encerra socket
  socket.close();

  return 0;
}

Servidor:

#include <iostream>
#include <array>
#include <boost/asio.hpp>
#include <noise/protocol.h>
#include <noise/handshake.h>

using boost::asio::ip::tcp;

int main(int argc, char *argv[]) {

    // Declara as chaves públicas estáticas local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_static_public_key;
    local_static_public_key.fill(0x55);
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_static_public_key;
    remote_static_public_key.fill(0xAA);

    // Declara as chaves públicas efêmeras local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_ephemeral_public_key;
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_ephemeral_public_key;

    // Declara as chaves privadas efêmeras local e remota
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> local_ephemeral_private_key;
    std::array<uint8_t, NOISE_PUBLIC_KEY_LEN> remote_ephemeral_private_key;

    // Declara o estado da negociação de chave
    NoiseHandshakeState *state = 0;

    // Inicializa o estado da negociação de chave
    int result = noise_handshakestate_new_by_name(
        &state, "Noise_NN_25519_ChaChaPoly_BLAKE2s", 0);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error initializing handshake state" << std::endl;
        return 1;
    }

    // Define as chaves públicas estáticas local e remota
    noise_handshakestate_set_local_static_public_key(state, local_static_public_key.data(), local_static_public_key.size());
    noise_handshakestate_set_remote_static_public_key(state, remote_static_public_key.data(), remote_static_public_key.size());

    // Gera o par de chaves efêmeras local
    result = noise_handshakestate_generate_local_keypair(state, local_ephemeral_private_key.data(), local_ephemeral_private_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error generating local ephemeral key pair" << std::endl;
        return 1;
    }
    noise_handshakestate_get_local_public_key(state, local_ephemeral_public_key.data(), local_ephemeral_public_key.size());

    // Cria um objeto boost::asio::io_context
    boost::asio::io_context io_context;

    // Cria um objeto boost::asio::ip::tcp::acceptor
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 1234));

    // Aguarda uma conexão do cliente
    tcp::socket socket(io_service);
    acceptor.accept(socket);

    // Recebe a chave pública efêmera local do cliente
    boost::asio::read(socket, boost::asio::buffer(remote_ephemeral_public_key));
    noise_handshakestate_set_remote_ephemeral_public_key(state, remote_ephemeral_public_key.data(), remote_ephemeral_public_key.size());

    // Gera o par de chaves efêmeras local
    result = noise_handshakestate_generate_local_keypair(state, local_ephemeral_private_key.data(), local_ephemeral_private_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error generating local ephemeral private key" << std::endl;
        return 1;
    }
    noise_handshakestate_get_local_public_key(state, local_ephemeral_public_key.data(), local_ephemeral_public_key.size());

    // Envia a chave pública efêmera local ao cliente
    boost::asio::write(socket, boost::asio::buffer(local_ephemeral_public_key));

    // Realiza a negociação de chave (handshake)
    size_t message_len;
    std::array<uint8_t, MAX_HANDSHAKE_MESSAGE_LEN> message;

    // Recebe a primeira mensagem do cliente
    boost::asio::read(socket, boost::asio::buffer(message));
    result = noise_handshakestate_read_message(state, message.data(), message.size(), remote_ephemeral_public_key.data(), &remote_ephemeral_public_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error to read first handshake message" << std::endl;
        return 1;
    }

    result = noise_handshakestate_write_message(state, remote_ephemeral_public_key.data(), remote_ephemeral_public_key.size(), message.data(), &message_len);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error to write first handshake message" << std::endl;
        return 1;
    }
    // Envia a primeira mensagem ao cliente
    boost::asio::write(socket, boost::asio::buffer(message, message_len));
    // Recebe a segunda mensagem do cliente
    boost::asio::read(socket, boost::asio::buffer(message));
    result = noise_handshakestate_read_message(state, message.data(), message.size(), remote_ephemeral_public_key.data(), &remote_ephemeral_public_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error to read second handshake message" << std::endl;
        return 1;
    }
    
    result = noise_handshakestate_write_message(state, remote_ephemeral_public_key.data(), remote_ephemeral_public_key.size(), message.data(), &message_len);
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error to write first handshake message" << std::endl;
        return 1;
    }
    // Envia a segunda mensagem ao cliente
    boost::asio::write(socket, boost::asio::buffer(message, message_len));

    // Gera a chave privada efêmera remota
    result = noise_handshakestate_generate_remote_key(state, remote_ephemeral_private_key.data(), remote_ephemeral_private_key.size());
    if (result != NOISE_ERROR_NONE) {
        std::cerr << "Error generating remote ephemeral private key" << std::endl;
        return 1;
    }


  // Libera o estado do handshake
  noise_handshakestate_free(state);

  // Encerra socket
  socket.close();

  return 0;
}