Cliente / Server com Boost.Asio

Neste post vamos mostrar como criar um cliente/servidor com Boost.Asio. Para quem não conhece, boost é uma library para programação network cross-plataform. Então para este exemplo, você deve estar certo que tenha as libraries do Boost. Caso não tenha, rode o seguinte comando para instalar emj uma distribuição Debian-like:

sudo apt-get install libboost-all-dev

E para instalar em uma dsitribuição Red Hat-like:

sudo yum install boost-devel

O Boost.Asio e outras libraries podem ser instalado separadamente, mas como neste artigo à intenção é mostrar como utilizar o asio, não o instalaremos com mais detalhes. Então vamos lá para nosso server:

Explicação do código:


É responsável por facilitar as operações assincronas, ele é quem faz a mágica entre software e OS. Outro ponto importante a ser citado é que ele trabalha em casos onde não exista threads explicitamente criadas, e as cria para você, caso necessário.


Aqui nós criamos os dados para nossa conexão, passando o IP e Porta. Já tcp::v4() é indica que utilizaremos uma conexão TCP/IPv4. 2000 é a porta em que vamos conectar.

Assim criamos uma acceptor para “escutar“ novas conexões.

Temos que criar um Handler para ser passado para funções async_write e async_read_some. Usamos uma função lambda neste exemplo, mas poderia ser feito de uma forma talvez mais clara com uma declaração de função void(const boost::system:error_code&, std::size_t);

Bom, seguindo em frente

Assim chegamos ao ponto onde criamos nosso socket para aceitar conexão de clientes.

Então criamos nosso buffer, para receber os dados enviados pelo cliente. Vamos utilizar um número máximo de 1024 bytes para escrita, então chamamos o async_write, para realizar a escrita no char msg[_size], também é passado nosso handler , em seguida fazemos a leitura no async_read_some e imprimimos o valor.


Agora indicamos ao io_service que nosso trabalho foi concluído. E isso é oque precisamos saber sobre no server com Boost.Asio. Para compilar com gcc execute faça o seguinte:

g++ -o server server.cxx -std=c++0x -lboost_system

No cliente vamos criar a conexão com o nosso server, passando os dados de conexão e mensagem por CLI. Vamos ao código:

Explicações:


Verificamos primeiro se todos os argumentos foram passados, caso não tenha todos os parâmetros, termina execução.


Como no server, temos que criar um io_service para nossas ações assíncronas, e também um socket. No tcp::resolver ele tem o trabalho de criar os dados de um endpoint e assim fazermos a conexão com o server com o boost::asio::connect, que recebe nosso socket e também um resolve, que recebe os valores de IP e Porta passados pelo CLI.


Criamos nossa varável e um tamanho máximo para o buffer, e então fazemos a escrita. Agora compilamos nosso cliente:

g++ -o client client.cxx -std=c++0x -lboost_system -lpthread

Assim podemos rodar o server e abrir um cliente

./server

Então enviamos dados de um cliente

./client 127.0.0.1 2000 “Exemplo simples com Boost.Asio”

E por fim criamos um Cliente/Servidor cross-plataform com facilidade. Essa foi uma apresentação do Boost.Asio. Você pode se aprofundar muito mais, nessa incrível lib para programação network.

Obrigado!

Enviado pelo colaborador Willian Briotto.

  • Cara, não use std::function nesse exemplo. Use auto, não há necessidade nenhuma de você ter usado std::function nesse exemplo. E tua expressão lambda captura variáveis (&), mas tem um corpo vazio!!! Então você eliminou a possibilidade dela ser convertida para um ponteiro para função por razão nenhuma.

    > […] No cliente não vamos criar a conexão com o nosso server […] assim fazermos a conexão com o server com o boost::asio::connect […]

    Parece contraditório.

    • Willian Briotto

      Então, talvez se eu tivesse usado auto, acredito que o exemplo não teria ficado claro para o leitor, por isso essa declaração. Eu particularmente não uso ponteiros para função, prefiro declarações de função explicitas ou lambdas(preferência).

      A correção do texto “contraditório” vai ser feita.

      >No cliente vamos criar a conexão com o nosso server

      • Tudo bem, não expliquei o suficiente o problema do std::function. Esse exemplo não precisa de std::function e quando você faz uso de std::function, você pede ao compilador que ele use std::function, que permite, entre outras coisas, “reatribuição de função/functor”. Você não usa isso, então não precisa disso. O fato de você ter especificado isso no código, entretanto, faz com que você pague por todos os custos que a abstração trás em tempo de execução, em vez de simplesmente usar a abstração de custo zero.

        Quando você usa auto, você deixa que o compilador faça inferência de tipos, então não vai ser std::function (e nem vai ser ponteiro para função). Vai ser o tipo do valor/functor retornado pela expressão lambda.

        • Willian Briotto

          Hum, entendi sua análise! No momento da criação me importei com simplicidade do códio e compreensão do leitor, e ababei me equivocando em termos técnicos. Obrigado pela análise, foi muito boa e técnica!

  • Cara, não use std::function nesse exemplo. Use auto, não há necessidade nenhuma de você ter usado std::function nesse exemplo. E tua expressão lambda captura variáveis (&), mas tem um corpo vazio!!! Então você eliminou a possibilidade dela ser convertida para um ponteiro para função por razão nenhuma.

    > […] No cliente não vamos criar a conexão com o nosso server […] assim fazermos a conexão com o server com o boost::asio::connect […]

    Parece contraditório.

    • Willian Briotto

      Então, talvez se eu tivesse usado auto, acredito que o exemplo não teria ficado claro para o leitor, por isso essa declaração. Eu particularmente não uso ponteiros para função, prefiro declarações de função explicitas ou lambdas(preferência).

      A correção do texto “contraditório” vai ser feita.

      >No cliente vamos criar a conexão com o nosso server

      • Tudo bem, não expliquei o suficiente o problema do std::function. Esse exemplo não precisa de std::function e quando você faz uso de std::function, você pede ao compilador que ele use std::function, que permite, entre outras coisas, “reatribuição de função/functor”. Você não usa isso, então não precisa disso. O fato de você ter especificado isso no código, entretanto, faz com que você pague por todos os custos que a abstração trás em tempo de execução, em vez de simplesmente usar a abstração de custo zero.

        Quando você usa auto, você deixa que o compilador faça inferência de tipos, então não vai ser std::function (e nem vai ser ponteiro para função). Vai ser o tipo do valor/functor retornado pela expressão lambda.

        • Willian Briotto

          Hum, entendi sua análise! No momento da criação me importei com simplicidade do códio e compreensão do leitor, e ababei me equivocando em termos técnicos. Obrigado pela análise, foi muito boa e técnica!

  • Cara, não use std::function nesse exemplo. Use auto, não há necessidade nenhuma de você ter usado std::function nesse exemplo. E tua expressão lambda captura variáveis (&), mas tem um corpo vazio!!! Então você eliminou a possibilidade dela ser convertida para um ponteiro para função por razão nenhuma.

    > […] No cliente não vamos criar a conexão com o nosso server […] assim fazermos a conexão com o server com o boost::asio::connect […]

    Parece contraditório.

    • Willian Briotto

      Então, talvez se eu tivesse usado auto, acredito que o exemplo não teria ficado claro para o leitor, por isso essa declaração. Eu particularmente não uso ponteiros para função, prefiro declarações de função explicitas ou lambdas(preferência).

      A correção do texto “contraditório” vai ser feita.

      >No cliente vamos criar a conexão com o nosso server

      • Tudo bem, não expliquei o suficiente o problema do std::function. Esse exemplo não precisa de std::function e quando você faz uso de std::function, você pede ao compilador que ele use std::function, que permite, entre outras coisas, “reatribuição de função/functor”. Você não usa isso, então não precisa disso. O fato de você ter especificado isso no código, entretanto, faz com que você pague por todos os custos que a abstração trás em tempo de execução, em vez de simplesmente usar a abstração de custo zero.

        Quando você usa auto, você deixa que o compilador faça inferência de tipos, então não vai ser std::function (e nem vai ser ponteiro para função). Vai ser o tipo do valor/functor retornado pela expressão lambda.

        • Willian Briotto

          Hum, entendi sua análise! No momento da criação me importei com simplicidade do códio e compreensão do leitor, e ababei me equivocando em termos técnicos. Obrigado pela análise, foi muito boa e técnica!