W3docs

Java Datagram Sockets (UDP)

Envía y recibe datagramas UDP en Java con DatagramSocket y DatagramPacket.

Socket y ServerSocket usan TCP — un flujo confiable, ordenado y basado en conexiones. DatagramSocket usa UDP, el otro protocolo de transporte: sin conexión y basado en paquetes. No te "conectas"; disparas datagramas independientes a una dirección y esperas que lleguen. No hay negociación, ni ordenamiento, ni garantía de entrega — y a cambio, no hay sobrecarga de conexión y la latencia es muy baja.

Esta página cubre cuándo UDP es la opción correcta, las dos clases principales (DatagramSocket y DatagramPacket), un ejemplo completo de solicitud/respuesta que puedes ejecutar, y los errores más comunes que cometen los programadores UDP por primera vez. Si eres nuevo en las redes en Java, comienza con la introducción a las redes.

Cuándo UDP es la herramienta adecuada

UDP sacrifica confiabilidad por velocidad y simplicidad. Es adecuado cuando:

  • La pérdida ocasional es aceptable — audio/video en vivo, estado de juego, telemetría. Un fotograma perdido es mejor que uno retrasado.
  • Los mensajes son pequeños y autocontenidos — consultas DNS, sincronización de tiempo NTP.
  • Transmites a múltiples receptores mediante broadcast/multicast — TCP no puede hacerlo.

Si necesitas cada byte, en orden (transferencia de archivos, páginas web, bases de datos), usa TCP. Muchas aplicaciones implementan su propia fiabilidad ligera sobre UDP en lugar de pagar el costo completo de TCP.

Las dos clases

UDP en Java usa un par de clases:

  • DatagramSocket — el punto final desde el que haces send y en el que haces receive. No hay un "socket servidor" separado; la misma clase hace ambas cosas, porque no hay conexión que aceptar.
  • DatagramPacket — un datagrama: un buffer de bytes más, para enviar, la dirección y puerto de destino; para recibir, se rellena con la dirección y puerto del remitente y la longitud de los datos.
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

Un detalle crítico: después de receive(), lee exactamente in.getLength() bytes del buffer — el buffer tiene tamaño fijo pero el datagrama puede ser más corto. Establece setSoTimeout(ms) para que un paquete perdido no bloquee receive() indefinidamente.

Un ejemplo práctico: solicitud/respuesta UDP en loopback

Este programa ejecuta un receptor en un hilo en segundo plano que espera un datagrama y responde al remitente, mientras el hilo principal envía un datagrama y lee el acuse de recibo — un viaje de ida y vuelta UDP completo en la interfaz de loopback.

java— editable, runs on the server

Lo que hay que observar en la ejecución:

  • No hubo connect() ni accept(). Ambos extremos son simplemente DatagramSockets; el emisor disparó un paquete a una dirección y puerto, y el receptor lo recogió. UDP no tiene conexión, por lo que la misma clase maneja ambos roles — la asimetría de los sockets cliente/servidor de TCP desaparece.
  • Un DatagramPacket transportó tanto los datos como el direccionamiento. El receptor supo quién envió la solicitud mediante request.getAddress() y request.getPort() y respondió directamente a ese punto — no hay canal persistente, por lo que cada respuesta debe ser dirigida explícitamente.
  • El cuerpo se decodificó con new String(data, 0, getLength(), …), no con todo el buffer de 1024 bytes. Un datagrama ocupa solo parte de un buffer fijo; leer getLength() bytes es obligatorio, o se añaden datos basura del espacio de buffer no utilizado.
  • setSoTimeout(2000) protegió el receive(). Como UDP no garantiza nada, una respuesta perdida bloquearía para siempre; un timeout convierte "el paquete nunca llegó" en una SocketTimeoutException que se puede reintentar o reportar.
  • El intercambio funcionó aquí porque el loopback no tiene pérdidas ni reordenamientos, pero la API no hizo tal promesa. En una red real, este datagrama podría desaparecer, llegar dos veces, o llegar después de uno posterior — que es exactamente por qué las aplicaciones sensibles a la fiabilidad eligen TCP o construyen su propio esquema de acuses de recibo sobre UDP.

Errores comunes

Algunas trampas afectan a casi todos la primera vez que usan DatagramSocket:

  • Leer todo el buffer en lugar de getLength() bytes. El buffer tiene tamaño fijo; el datagrama normalmente no. Siempre recorta con new String(data, 0, packet.getLength(), …) o Arrays.copyOf(data, packet.getLength()). Reutilizar un buffer empeora esto — los bytes sobrantes de un datagrama anterior más largo aparecen como basura al final.
  • Sin timeout en receive(). Como UDP nunca garantiza la entrega, un paquete perdido deja receive() bloqueado para siempre. Llama a setSoTimeout(ms) y maneja la SocketTimeoutException resultante (reintenta, registra o abandona).
  • Enviar más de lo que cabe en un datagrama. UDP no tiene streaming; un send() es un paquete. Las cargas útiles grandes son fragmentadas por IP y un solo fragmento perdido descarta todo el datagrama. Mantén las cargas pequeñas — aproximadamente 512 bytes es un límite seguro que evita la fragmentación en la mayoría de las redes.
  • Olvidar cerrar el socket. Un DatagramSocket ocupa un puerto del sistema operativo. Usa try-with-resources (implementa AutoCloseable) o ciérralo en un bloque finally para liberar el puerto.
  • Asumir que la respuesta proviene de donde enviaste. receive() sobreescribe la dirección y el puerto del paquete con los del remitente real. Responde siempre usando packet.getAddress()/packet.getPort() en lugar de un destino codificado.

Para entrega garantizada y ordenada, usa las clases TCP en Java sockets.

Práctica

Práctica
Un agente de monitoreo recibe datagramas UDP en un buffer 'byte[2048]' reutilizado mediante 'socket.receive(packet)', luego convierte todo el buffer con 'new String(packet.getData(), StandardCharsets.UTF_8)'. Los mensajes cortos salen con basura al final. ¿Cuál es la solución?
Un agente de monitoreo recibe datagramas UDP en un buffer 'byte[2048]' reutilizado mediante 'socket.receive(packet)', luego convierte todo el buffer con 'new String(packet.getData(), StandardCharsets.UTF_8)'. Los mensajes cortos salen con basura al final. ¿Cuál es la solución?
Was this page helpful?