19 de mar. de 2007

Como fazer um broadcast via UDP usando Indy 9?

UDP é um protocolo de transporte de dados que se caracteriza por não garantir a entrega dos dados, o que permite entregar rapidamente os dados. Um uso frequente para UDP é transmissão de áudio ou vídeo, que é um caso onde a perda de um pacote de dados não compromete o resultado final.

Uma outra característica do protocolo UDP é a possibilidade de transmitir um mesmo pacote de dados para todas as máquina da rede em uma única operação, o que é conhecido como broadcast. Nesse caso, os dados são enviados por UDP para um endereço especial da rede que replicará o mesmo pacote de dados para todos os endereços da mesma subrede.

Um uso possível seria localizar numa rede local todas as máquinas que estão executando seu programa. Para isso, uma cópia do programa poderia iniciar um broadcast na rede pedindo que todas as outras cópias (em uso) do programa se identifiquem, por exemplo, informando o nome da máquina e o IP.

Usando os componentes TIdUDPClient e TIdUDPServer, o código seria algo assim:

1 type 2 TSeuForm = class(...) 3 // coloque um TIdUDPClient e TIdUDPServer no form 4 // por exemplo... 5 UDPClient: TIdUDPClient; 6 UDPServer: TIdUDPServer; 7 ... 8 procedure FormCreate(Sender: TObject); 9 procedure FormDestroy(Sender: TObject); 10 procedure UDPServerUDPRead(Sender: TObject; AData: TStream; 11 ABinding: TIdSocketHandle); 12 ... 13 private 14 procedure Ping; 15 ... 16 17 const 18 NOSSA_PORTA = 49152; // pode ser qualquer valor entre 49152 e 65535 19 20 // rotina de PING 21 // em algum lugar do programa isso é executado 22 procedure TSeuForm.Ping; 23 var 24 S: string; 25 IP: string; 26 X: Integer; 27 begin 28 // não queremos responder nosso próprio comando 29 UDPServer.Active := False; 30 31 // timeout da resposta, em milissegundos 32 UDPClient.ReceiveTimeout := 5000; 33 34 // manda o comando para todas as máquinas da rede 35 UDPClient.Broadcast('PING', NOSSA_PORTA); 36 37 // espera pela resposta 38 while True do begin 39 40 S := UDPClient.ReceiveString(IP, X); 41 if S = '' then begin 42 // ocorreu timeout 43 Break; 44 end; 45 // faz alguma coisa com os dados... 46 // - S tem o nome da máquina que respondeu 47 // - IP tem o IP da máquina que respondeu 48 end; 49 50 UDPServer.Active := True; 51 end; 52 53 // evento OnUDPRead do UDPServer 54 procedure TSeuForm.UDPServerUDPRead(Sender: TObject; AData: TStream; 55 ABinding: TIdSocketHandle); 56 var 57 Server: TIdUDPServer; 58 S: String; 59 begin 60 Server := Sender as TIdUDPServer; 61 62 // recupera o que recebeu 63 SetLength(S, AData.Size); 64 AData.Read(S[1], AData.Size); 65 66 // é o nosso comando de PING? 67 if S = 'PING' then begin 68 69 // envia a resposta 70 S := Server.LocalName; 71 ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, S[1], Length(S)); 72 end; 73 end; 74 75 // evento OnCreate do form 76 procedure TSeuForm.FormCreate(Sender: TObject); 77 begin 78 UDPServer.DefaultPort := NOSSA_PORTA; 79 UDPServer.Active := True; 80 ... 81 end; 82 83 // evento OnDestroy do form 84 procedure TSeuForm.FormDestroy(Sender: TObject); 85 begin 86 UDPServer.Active := False; 87 ... 88 end;

Para evitar conflitos com outros programas e serviços do Windows, procure escolher uma porta (linha 18) entre 49152 e 65535. Independente da porta escolhida, lembre-se que essa porta precisa estar liberada para UDP no seu firewall.

O valor de timeout usado na rotina de ping (linha 32) precisa ser ajustado de acordo com o tamanho da rede. Um valor muito pequeno pode fazer com que a rotina de ping desista antes que todas as máquinas respondam e um valor muito alto pode congelar momentaneamente o programa. Não tem outra forma de aguardar pela resposta das máquinas já que, a princípio, a rotina não sabe quantas máquinas vão responder.

Broadcast deve ser usado com moderação. O broadcast utiliza um endereço especial da rede para transmitir os dados que faz com que o mesmo pacote de dados seja enviado a todos os endereços possíveis da rede. Como isso consome recursos da rede, não é boa idéia utilizar isso sem que seja absolutamente necessário. É tentador colocar a rotina de ping num timer que executa a cada segundo, mas o administrador da sua rede não vai ficar muito feliz com essa idéia.

Eu não entendo muito de redes, mas é bom saber que alguns roteadores podem impedir o broadcast.

Links relacionados

16 de mar. de 2007

MSDN Magazine Abril/2007

A MSDN Magazine de Abril  já está disponível no site da Microsoft. Destaque para efeitos especiais com o novo gerenciador de janelas Aero Glass do Windows Vista e um pequeno analisador/parser XML em C++.