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 hacessendy en el que hacesreceive. 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 infoUn 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.
Lo que hay que observar en la ejecución:
- No hubo
connect()niaccept(). Ambos extremos son simplementeDatagramSockets; 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
DatagramPackettransportó tanto los datos como el direccionamiento. El receptor supo quién envió la solicitud medianterequest.getAddress()yrequest.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; leergetLength()bytes es obligatorio, o se añaden datos basura del espacio de buffer no utilizado. setSoTimeout(2000)protegió elreceive(). Como UDP no garantiza nada, una respuesta perdida bloquearía para siempre; un timeout convierte "el paquete nunca llegó" en unaSocketTimeoutExceptionque 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 connew String(data, 0, packet.getLength(), …)oArrays.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 dejareceive()bloqueado para siempre. Llama asetSoTimeout(ms)y maneja laSocketTimeoutExceptionresultante (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
DatagramSocketocupa un puerto del sistema operativo. Usa try-with-resources (implementaAutoCloseable) o ciérralo en un bloquefinallypara 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 usandopacket.getAddress()/packet.getPort()en lugar de un destino codificado.
Para entrega garantizada y ordenada, usa las clases TCP en Java sockets.