Java Datagram Sockets (UDP)
Envie e receba datagramas UDP em Java com DatagramSocket e DatagramPacket.
Socket e ServerSocket usam TCP — um fluxo confiável, ordenado e baseado em conexão. DatagramSocket usa UDP, o outro protocolo de transporte: sem conexão e baseado em pacotes. Você não "conecta"; você dispara datagramas independentes para um endereço e espera que cheguem. Não há handshake, nem ordenação, nem garantia de entrega — e em troca, nenhuma sobrecarga de conexão e latência muito baixa.
Esta página cobre quando UDP é a escolha certa, as duas classes principais (DatagramSocket e DatagramPacket), um exemplo completo de requisição/resposta que você pode executar e os erros comuns que afetam programadores UDP iniciantes. Se você é novo em redes em Java, comece com a introdução à rede.
Quando UDP é a ferramenta certa
UDP troca confiabilidade por velocidade e simplicidade. É adequado quando:
- Perdas ocasionais são aceitáveis — áudio/vídeo ao vivo, estado de jogo, telemetria. Um frame perdido é melhor do que um atrasado.
- As mensagens são pequenas e autocontidas — consultas DNS, sincronização de tempo NTP.
- Você faz broadcast/multicast para muitos receptores — TCP não consegue fazer isso.
Se você precisa de cada byte, em ordem (transferência de arquivos, páginas web, bancos de dados), use TCP. Muitas aplicações implementam sua própria confiabilidade leve sobre UDP em vez de pagar o custo total do TCP.
As duas classes
UDP em Java usa um par:
DatagramSocket— o endpoint do qual vocêsende no qual vocêreceive. Não existe um "socket servidor" separado; a mesma classe faz os dois, porque não há conexão a aceitar.DatagramPacket— um datagrama: um buffer de bytes mais, para envio, o endereço de destino e a porta; para recebimento, é preenchido com o endereço e a porta do remetente e o comprimento dos dados.
DatagramSocket socket = new DatagramSocket(9000); // bind to receive on 9000
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
DatagramPacket out = new DatagramPacket(data, data.length, address, port);
socket.send(out); // fire and forget
byte[] buf = new byte[1024];
DatagramPacket in = new DatagramPacket(buf, buf.length);
socket.receive(in); // blocks; fills buf + sender infoUm detalhe crítico: após receive(), leia exatamente in.getLength() bytes do buffer — o buffer tem tamanho fixo, mas o datagrama pode ser menor. Defina setSoTimeout(ms) para que um pacote perdido não bloqueie receive() para sempre.
Um exemplo completo: uma requisição/resposta UDP via loopback
Este programa executa um receptor em uma thread em segundo plano que aguarda um datagrama e responde ao remetente, enquanto a thread principal envia um datagrama e lê o reconhecimento — uma viagem de ida e volta UDP completa na interface de loopback.
O que observar na execução:
- Não houve
connect()nemaccept(). Ambos os lados são apenasDatagramSockets; o remetente disparou um pacote para um endereço e porta, e o receptor o capturou. UDP não tem conexão, então a mesma classe lida com ambos os papéis — a assimetria dos sockets cliente/servidor do TCP desaparece. - Um
DatagramPacketcarregou dados e endereçamento. O receptor descobriu quem enviou a requisição a partir derequest.getAddress()erequest.getPort()e respondeu diretamente para esse endpoint — não há canal persistente, então cada resposta deve ser endereçada explicitamente. - O corpo foi decodificado com
new String(data, 0, getLength(), …), não o buffer inteiro de 1024 bytes. Um datagrama preenche apenas parte de um buffer fixo; lergetLength()bytes é obrigatório, ou você acrescenta dados indesejados do espaço não utilizado do buffer. setSoTimeout(2000)protegeu oreceive(). Como UDP não garante nada, uma resposta perdida bloquearia para sempre; um timeout transforma "pacote nunca chegou" em umaSocketTimeoutExceptioncapturável que você pode tentar novamente ou relatar.- A troca funcionou aqui porque o loopback não tem perdas e é ordenado, mas a API não fez tal promessa. Sobre uma rede real, este datagrama poderia desaparecer, chegar duas vezes ou chegar após um posterior — que é exatamente por isso que aplicações sensíveis à confiabilidade escolhem TCP ou constroem seu próprio esquema de reconhecimento sobre UDP.
Erros comuns
Algumas armadilhas afetam quase todo mundo na primeira vez que usam DatagramSocket:
- Ler o buffer inteiro em vez de
getLength()bytes. O buffer tem tamanho fixo; o datagrama geralmente não. Sempre fatie comnew String(data, 0, packet.getLength(), …)ouArrays.copyOf(data, packet.getLength()). Reutilizar um buffer piora isso — bytes restantes de um datagrama anterior, mais longo, aparecem como dados indesejados no final. - Sem timeout no
receive(). Como UDP nunca promete entrega, um pacote perdido deixareceive()bloqueado para sempre. ChamesetSoTimeout(ms)e trate aSocketTimeoutExceptionresultante (tente novamente, registre ou desista). - Enviar mais do que cabe em um datagrama. UDP não tem streaming; um
send()é um pacote. Payloads grandes são fragmentados pelo IP e um único fragmento perdido derruba o datagrama inteiro. Mantenha os payloads pequenos — aproximadamente 512 bytes é um teto seguro que evita fragmentação na maioria das redes. - Esquecer de fechar o socket. Um
DatagramSocketocupa uma porta do sistema operacional. Use try-with-resources (ele implementaAutoCloseable) ou feche-o em um blocofinallypara que a porta seja liberada. - Assumir que a resposta vem de onde você enviou.
receive()sobrescreve o endereço e a porta do pacote com o remetente real. Sempre responda usandopacket.getAddress()/packet.getPort()em vez de um destino fixo.
Para entrega garantida e ordenada, use as classes TCP em Java sockets.