Del'Arco

The Pixel Company

Archive for the ‘resolver’ tag

boost::asio – Introdução

with 2 comments

boostApesar de pouco comentado (pelo menos nos forums que eu participo participava), Boost é um conjunto de bibliotecas que facilitam muito a vida do programador e , sem dúvidas, é o plus-plus do C++. Se vc ainda não conhece: http://www.boost.org/.

Para quem não sabe, Boost.Asio é uma biblioteca multiplataforma (cross-platform) para programação de rede/comunicação (network) e operações de entrada e saída em baixo nível (low-level I/O) que oferece, aos desenvolvedores, um modelo consistente e assíncrono.

(Essa definição foi descaradamente traduzida da encontrada no site oficial)

Então, o que eu posso fazer com isso?

Você pode, por exemplo, usar sockets (TCP, UDP e ICMP), Timers e criar/manipular portas seriais.

Mas eu sei fazer isso! É só incluir o winsock.h e… no linux é fácil tb…

Não ter que se preocupar com o startup e cleanup do Winsock e não ter que colocar 8463763 #ifdef’s no meio do código já é um bom motivo para usar Boost.Asio. Se vc escrever “socket.read_some(…);“, vai funcionar em qualquer plataforma suportada (Win32/64, Linux kernel 2.4+, Solaris, Mac OS X 10.4+).

Beleza! Cadê os exemplos?

Antes disso, para não ficar perdido, é necessário entender como o Boost.Asio trabalha. Ao inves de oferecer um modelo genérico aproximado ao que há de comum entre as várias plataformas, a biblioteca apresenta uma nova proposta: para efetuar operações de entrada e saída seu programa precisa de, no mínimo, um objeto io_service (boost::asio::io_service). O objeto io_service funciona como um intermediário entre o seu software e o sistema operacional. Overhead? Tenha certeza que o boost faz esse intermédio da forma mais eficiente possível e, até hoje, nunca vi ninguém reclamando sobre o desempenho da biblioteca. A imagem abaixo (que foi retirada do site oficial) mostra o funcionamento do Boost.Asio para operações síncrona:

sync_op1 – O programa inicia a conexão chamando o objeto i/o (socket, por exemplo);

2 – O objeto i/o encaminha a requisição de conexão para o objeto io_service;

3 – O objeto io_service faz uma chamada ao sistema operacional para realizar a conexão;

4 – O sistema operacional retorna um resultado para o objeto io_service;

5 – O objeto io_service “traduz” qualquer erro resultante na operação e retorna ao objeto i/o;

6 – O objeto i/o retorna o resultado para o programa.

.

.

.

.

.

O modelo para operações assíncronas é representado pelo diagrama ao lado: async_op

Ao invés de esperar cada etapa do modelo síncrono, aqui podemos passar uma função handler que será chamada assim que a operação for concluída, independente da ocorrência de erros.

As três primeiras etapas são praticamente idênticas ao modelo anterior (do ponto de vista do usuário/programador):

1 – O programa inicia a conexão chamando o objeto i/o (socket, por exemplo);

2 – O objeto i/o encaminha a requisição de conexão para o objeto io_service;

3 – O objeto io_service faz uma chamada ao sistema operacional para realizar a conexão;

Após as etapas indicadas, o sistema operacional indica o resultado da operação colocando-o em uma fila que será lida pelo objeto io_service, que por sua vez “traduz” qualquer mensagem de erro e chama a função handler passada como parâmetro pelo programa.

É importante comentar que, para que o objeto io_service possa tratar as tarefas/operações assíncronas pendentes, é necessário chamar io_service::run().

A função io_service::run() retornará assim que não houver mais tarefa assíncronas pendentes.

Okay! Então eu crio um objeto io_service e já era?

Aí é que está a grande maravilha do modelo proposto: usando vários objetos io_service (um em cada thread) e dividindo as operações entre eles, permitimos ao sistema operacional também dividí-las entre os vários núcleo disponíveis!

Tá… e os exemplos?

Timer

Para um wait síncrono (só retorna depois de completo):

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

using boost::asio::io_service;
using boost::asio::deadline_timer;

int main(int ac, char ** av)
{
    io_service ios;

    deadline_timer t(ios);
    t.expires_from_now(boost::posix_time::seconds(5));

    std::cout << "Esperando 5 segundos..." << std::endl;

    t.wait();

    std::cout << "Continua..." << std::endl;
    return 0;
}

Para um timer assíncrono, precisamos de um handler:


void handler(boost::system::error_code & ec)
{
	if(ec)
		//erro
	else
		//ok
}

Então, podemos usar o wait assíncrono:

t.expires_from_now(boost::posix_time::milliseconds(400));
t.async_wait(handler);
ios.run();

Um exemplo de timer assíncrono:

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

using boost::asio::io_service;
using boost::asio::deadline_timer;

void handler(int segundo, boost::system::error_code & ec)
{
	if(!ec)
		std::cout << "Esperando... " << segundo << std::endl;
}

int main(int ac, char ** av)
{
	io_service ios;

	for(int i = 1; i <= 5; i++)
	{
		deadline_timer * t = new deadline_timer(ios);
		t->expires_from_now(boost::posix_time::seconds(i));
		t->async_wait(boost::bind(&handler, i, boost::asio::placeholders::error));
	}

	ios.run();

	std::cout << "Continua..." << std::endl;
	return 0;
}

Repare que não me preocupei em manter uma referência para os objetos criados dinamicamente (para apagá-los posteriormente), pois o programa é terminado logo após o retorno do último timer assíncrono.

Socket

Um exemplo simples de socket (tcp) que se conecta ao site:

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

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

int main(int ac, char ** av)
{
    io_service ios;

    tcp::socket sck(ios);
    tcp::endpoint endp(boost::asio::ip::address::from_string("64.191.3.101"), 80);

    try
    {
        sck.connect(endp);
        std::cout << "Conectado!" << std::endl;
    }
    catch(boost::system::system_error & error)
    {
        std::cout << "Erro ao conectar: " << error.what() << std::endl;
    }

    std::cout << "Continua..." << std::endl;
    return 0;
}

Ou um resolver:

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

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

int main(int ac, char ** av)
{
    io_service ios;

    tcp::resolver resolver(ios);
    tcp::resolver::query query("blog.delarco.com.br", "http");
    tcp::resolver::iterator iter = resolver.resolve(query);
    tcp::resolver::iterator end;

    while (iter != end)
    {
        tcp::endpoint endpoint = *iter++;
        std::cout << endpoint << std::endl;
    }

    return 0;
}
#include <iostream>
#include <boost/asio.hpp>using boost::asio::io_service;
using boost::asio::deadline_timer;int main(int ac, char ** av)
{
io_service ios;

deadline_timer t(ios);
t.expires_from_now(boost::posix_time::seconds(5));

std::cout << “Esperando 5 segundos…” << std::endl;

t.wait();

std::cout << “Continua…” << std::endl;

return 0;
}

Written by Leandro Del'Arco

novembro 18th, 2009 at 3:49 pm