W3docs

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ê send e 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 info

Um 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.

java— editable, runs on the server

O que observar na execução:

  • Não houve connect() nem accept(). Ambos os lados são apenas DatagramSockets; 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 DatagramPacket carregou dados e endereçamento. O receptor descobriu quem enviou a requisição a partir de request.getAddress() e request.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; ler getLength() bytes é obrigatório, ou você acrescenta dados indesejados do espaço não utilizado do buffer.
  • setSoTimeout(2000) protegeu o receive(). Como UDP não garante nada, uma resposta perdida bloquearia para sempre; um timeout transforma "pacote nunca chegou" em uma SocketTimeoutException capturá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 com new String(data, 0, packet.getLength(), …) ou Arrays.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 deixa receive() bloqueado para sempre. Chame setSoTimeout(ms) e trate a SocketTimeoutException resultante (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 DatagramSocket ocupa uma porta do sistema operacional. Use try-with-resources (ele implementa AutoCloseable) ou feche-o em um bloco finally para 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 usando packet.getAddress()/packet.getPort() em vez de um destino fixo.

Para entrega garantida e ordenada, use as classes TCP em Java sockets.

Prática

Prática
Um agente de monitoramento recebe datagramas UDP em um buffer 'byte[2048]' reutilizado via 'socket.receive(packet)', depois converte o buffer inteiro com 'new String(packet.getData(), StandardCharsets.UTF_8)'. Mensagens curtas saem com dados indesejados no final. Qual é a correção?
Um agente de monitoramento recebe datagramas UDP em um buffer 'byte[2048]' reutilizado via 'socket.receive(packet)', depois converte o buffer inteiro com 'new String(packet.getData(), StandardCharsets.UTF_8)'. Mensagens curtas saem com dados indesejados no final. Qual é a correção?
Was this page helpful?