UDP Broadcast

Esse é o 12º artigo da série IPC - esse artigo demonstra o uso do IPC UDP Broadcast

Introdução

O UDP no modo broadcast permite enviar mensagens para todas as máquinas conectadas na rede de uma única vez, para exemplificar tome a televisão como exemplo, o sinal de TV é transmitido pelo ar onde qualquer televisão sintonizada nessa determinada frequência pode captar o programa transmitido, esse tipo de aplicação seria inviável se o sinal fosse enviado para cada televisor existente. O broadcast está presente somente no protocolo IPv4, no IPv6 é usado uma outra técnica conhecida como multicast. O broadcast é usado no protocolo ARP(Address Resolution Protocol) que mapeia o endereço físico, o endereço MAC.

Endereço Broadcast

Para entender o endereço broadcast é adotado ip da classe C onde o primeiro octeto tem um range de 192 até 223, normalmente as redes domésticas utilizam essa classe como por exemplo 192.168.0.XXX. Na classe C quando a rede é descrita na forma 192.168.0.XXX, não devemos usar os valores 0 e 255, onde 0 representa a rede e o 255 representa o endereço broadcast dessa rede, sendo assim se uma mensagem for enviada para esse endereço todas as máquinas conectadas nessa rede irá receber a mensagem.

Representação do Broadcast na rede

Quando uma mensagem é enviada para esse endereço, todas as máquinas irão receber a mensagem mesmo que não esteja interessada. Para ilustrar, o exemplo representa uma mensagem broadcast enviada em uma rede classe B

Jq190

Na imagem é possível notar que as mensagem não é propagada pelo roteador

Identificando o endereço de broadcast

Para identificar qual o endereço broadcast da rede pode ser usado o comando ip

Nas interfaces do computador é possível notar o ip que é atribuído para a máquina e em seguida o endereço de broadcast logo a frente do acrônimo brd(broadcast), esse é o endereço que é usado para enviar mensagens broadcast

Obs: Durante o exemplo é necessário inserir esse endereço correspondente a rede em que está rodando o exemplo no descritor usado em button_process

Preparação do Ambiente

Antes de apresentarmos o exemplo, primeiro é necessário instalar algumas ferramentas para auxiliar na análise da comunicação. As ferramentas necessárias para esse artigo são o tcpdump e o netcat(nc), para instalá-las basta executar os comandos abaixo:

netcat

O netcat é uma ferramenta capaz de interagir com conexões UDP e TCP, podendo abrir conexões, ouvindo como um servidor, ou com cliente enviando mensagens para um servidor.

tcpdump

O tcpdump é uma ferramenta capaz de monitorar o tráfego de dados em uma dada interface como por exemplo eth0, com ele é possível analisar os pacotes que são recebido e enviados.

Implementação

Para demonstrar o uso desse IPC, iremos utilizar o modelo Cliente/Servidor, onde o processo Cliente(button_process) vai enviar uma mensagem via broadcast para o Servidor(led_process) que vai ler a mensagem e verificar se corresponde com os comandos cadastrados internamente, aplicando o comando caso seja válido.

Biblioteca

A biblioteca criada permite uma fácil criação do servidor, sendo o servidor orientado a eventos, ou seja, fica aguardando as mensagens chegarem.

udp_broadcast_receiver.h

Primeiramente é criado um callback responsável por eventos de recebimento, essa função será chamada quando houver esse evento.

É criado também um contexto que armazena os parâmetros utilizados pelo servidor, sendo o socket para armazenar a instância criada, port que recebe o número que corresponde onde o serviço será disponibilizado, buffer que aponta para a memória alocada previamente pelo usuário, buffer_size o representa o tamanho do buffer e o callback para recepção da mensagem

Essa função inicializa o servidor com os parâmetros do contexto

Essa função aguarda uma mensagem enviada pelo cliente.

udp_broadcast_receiver.c

No UDP_Broadcast_Receiver_Init é definido algumas variáveis para auxiliar na inicialização do servidor, sendo uma variável booleana que representa o estado da inicialização do servidor, uma variável do tipo inteiro para habilitar o reuso da porta caso o servidor precise reiniciar e uma estrutura sockaddr_in que é usada para configurar o servidor para se comunicar através da rede.

Para realizar a inicialização é criado um dummy do while, para que quando houver falha em qualquer uma das etapas sairá da função com status de erro, nesse ponto é verificado se o contexto, o buffer e se o tamanho do buffer foi inicializado, sendo sua inicialização de responsabilidade do usuário

É criado um endpoint com o perfil de se conectar via protocolo IPv4(AF_INET), do tipo datagram que caracteriza o UDP(SOCK_DGRAM), o último parâmetro pode ser 0 nesse caso.

A estrutura é preenchida com parâmetros fornecidos pelo usuário como em qual porta que o serviço vai rodar.

Aqui é habilitado o reuso do socket caso necessite reiniciar o serviço

Neste ponto é aplicado as configurações ao socket criado e é atribuído true na variável status

Na função UDP_Broadcast_Receiver_Run é declarado algumas variáveis para receber as mensagens por meio do broadcast

É verificado se o socket é válido e aguarda o envio de uma mensagem do cliente, a mensagem é passada para o callback realizar o tratamento de acordo com a aplicação do cliente, e o status é retornado.

udp_broadcast_sender.h

É criado também um contexto que armazena os parâmetros utilizados pelo cliente, sendo o socket para armazenar a instância criada, hostname é o IP broadcast onde vão ser enviadas as mensagens e o port que recebe o número que corresponde qual o serviço deseja consumir

Inicializa o cliente com os parâmetros preenchidos no descritor

Envia mensagem para o servidor baseado nos parâmetros do descritor.

udp_broadcast_sender.c

Na função UDP_Broadcast_Sender_Init é verificado se o contexto foi iniciado, o socket é configurado como UDP e é habilitado o envio no modo broadcast

Na função UDP_Broadcast_Sender_Send é definido algumas variáveis para auxiliar na comunicação com o servidor, sendo uma variável booleana que representa o estado de envio para o servidor, uma estrutura sockaddr_in que é usada para configurar o servidor no qual será enviado as mensagens e uma variável de quantidade de dados enviados.

A estrutura é parametrizada com os dados do servidor

Aqui é realizado o envio da mensagem para o endereço broadcast

Aplicação é composta por três executáveis sendo eles:

  • launch_processes – é responsável por lançar os processos button_process e led_process através da combinação fork e exec
  • button_interface – é responsável por ler o GPIO em modo de leitura da Raspberry Pi e escrever o estado interno no arquivo
  • led_interface – é responsável por ler do arquivo o estado interno do botão e aplicar em um GPIO configurado como saída

launch_processes

No main é criada duas variáveis para armazenar o PID do button_process e do led_process, e mais duas variáveis para armazenar o resultado caso o exec venha a falhar.

Em seguida é criado um processo clone, se processo clone for igual a 0, é criado um array de strings com o nome do programa que será usado pelo exec, em caso o exec retorne, o estado do retorno é capturado e será impresso no stdout e aborta a aplicação. Se o exec for executado com sucesso o programa button_process será carregado.

O mesmo procedimento é repetido novamente, porém com a intenção de carregar o led_process.

button_interface

É definida uma lista de comandos que para o enviar os comandos

A implementação do Button_Run ficou simples, onde é realizada a inicialização do interface de botão e o programa fica em loop aguardando o pressionamento do botão para alterar o estado da variável e enviar a mensagem para o IP broadcast

led_interface

A implementação do LED_Run ficou simplificada, é realizada a inicialização da interface de LED, do servidor e o programa fica em loop aguardando o recebimento de uma mensagem.

button_process

A parametrização do cliente fica por conta do processo de botão que inicializa o contexto com o endereço broadcast, o serviço que deseja consumir, e assim passamos os argumentos para Button_Run iniciar o processo.

led_process

A parametrização do servidor fica por conta do processo de LED que inicializa o contexto com o buffer, seu tamanho, a porta onde vai servir e o callback preenchido, e assim passamos os argumentos para LED_Run iniciar o serviço.

A implementação no evento de recebimento da mensagem, compara a mensagem recebida com os comandos internos para o acionamento do LED, caso for igual executa a ação correspondente.

Compilando, Executando e Matando os processos

Para compilar e testar o projeto é necessário instalar a biblioteca de hardware necessária para resolver as dependências de configuração de GPIO da Raspberry Pi.

Compilando

Para facilitar a execução do exemplo, o exemplo proposto foi criado baseado em uma interface, onde é possível selecionar se usará o hardware da Raspberry Pi 3, ou se a interação com o exemplo vai ser através de input feito por FIFO e o output visualizado através de LOG.

Clonando o projeto

Pra obter uma cópia do projeto execute os comandos a seguir:

Selecionando o modo

Para selecionar o modo devemos passar para o cmake uma variável de ambiente chamada de ARCH, e pode-se passar os seguintes valores, PC ou RASPBERRY, para o caso de PC o exemplo terá sua interface preenchida com os sources presentes na pasta src/platform/pc, que permite a interação com o exemplo através de FIFO e LOG, caso seja RASPBERRY usará os GPIO’s descritos no artigo.

Modo PC

Modo RASPBERRY

Executando

Para executar a aplicação execute o processo launch_processes para lançar os processos button_process e led_process que foram determinados de acordo com o modo selecionado.

Uma vez executado podemos verificar se os processos estão rodando atráves do comando

O output

Interagindo com o exemplo

Dependendo do modo de compilação selecionado a interação com o exemplo acontece de forma diferente

MODO PC

Para o modo PC, precisamos abrir um terminal e monitorar os LOG’s

Dessa forma o terminal irá apresentar somente os LOG’s referente ao exemplo.

Para simular o botão, o processo em modo PC cria uma FIFO para permitir enviar comandos para a aplicação, dessa forma todas as vezes que for enviado o número 0 irá logar no terminal onde foi configurado para o monitoramento, segue o exemplo

Output do LOG quando enviado o comando algumas vezez

MODO RASPBERRY

Para o modo RASPBERRY a cada vez que o botão for pressionado irá alternar o estado do LED.

Monitorando o tráfego usando o tcpdump

Para monitorar as mensagens que trafegam, precisamos ler uma interface, para saber quais interfaces que o computador possui usamos o comando

Output

Como podemos ver temos 5 interfaces no computador onde o comando foi executado, pode ser que a máquina que esteja usando possa ter mais interfaces ou menos interfaces. Para teste local, iremos usar a interface local denominada lo, que representa a interface de loopback.

O tcpdump possui opções que permite a visualização dos dados, não irei explicar tudo, fica de estudo para quem quiser saber mais sobre a ferramenta. Executando o comando podemos ver todas as mensagens de broadcast

Após executar o comando o tcpdump ficará fazendo sniffing da interface, tudo o que for trafegado nessa interface será apresentado, dessa forma enviamos um comando e veremos a seguinte saída:

Podemos ver que não há o processo de handshake somente o envio da mensagem, como descrito a seguir:

  • No instante 16:30:53.482390 IP 192.168.0.140.39611 > 192.168.0.255.1234 o cliente envia uma mensagem para o server via broadcast

Testando conexão com o servidor via netcat

A aplicação realiza a comunicação entre processos locais, para testar uma comunicação remota usaremos o netcat que permite se conectar de forma prática ao servidor e enviar os comandos. Para se conectar basta usar o seguinte comando:

Como descrito no comando ip usaremos o ip de broadcast apresentado na interface enp0s31f6 que é o IP 192.168.0.255, então o comando fica

E enviamos o comando LED ON, se visualizar no log irá apresentar que o comando foi executado, para monitorar com o tcpdump basta mudar a interface

Matando os processos

Para matar os processos criados execute o script kill_process.sh

Conclusão

O broadcast é uma boa solução para enviar mensagens de uma única vez para todas as máquinas, porém dependendo da frequência em que essa mensagem é disseminada pode causar congestionamento na rede, causando uma queda de desempenho, e enviando mensagens para máquinas que não estejam interessados nesses dados. Para resolver esse problema existe um modo de envio conhecido como Multicast que será abordado no próximo artigo.

Referência

Outros artigos da série

<< Socket UDPUDP Multicast >>
Notificações
Notificar
guest
0 Comentários
Inline Feedbacks
View all comments

WEBINAR

Visão Computacional para a redução de erros em processos manuais

DATA: 23/09 ÀS 17:00 H