UDP (User Datagram Protocol) es un protocolo de la capa de transporte que se caracteriza por ser “sin conexión” (connectionless). A diferencia de TCP, UDP no establece una sesión mediante un saludo de tres vías (handshake), ni garantiza que los datos lleguen, que lleguen en orden o que se entreguen sin errores. El protocolo simplemente empaqueta la información en datagramas y la envía a una dirección de destino.
Este comportamiento lo hace ideal para aplicaciones donde la velocidad y la baja latencia son críticas, como juegos en línea, streaming de video o servicios de DNS, donde perder un pequeño paquete es preferible a esperar por una retransmisión que retrasaría el flujo de datos. Sin embargo, si se usa incorrectamente en aplicaciones que requieren integridad total (como la transferencia de archivos), se corre el riesgo de pérdida de información o corrupción de mensajes, ya que el programador debe implementar manualmente la lógica de retransmisión o ordenamiento si fuera necesario.
Para trabajar con UDP en Rust, se utiliza std::net::UdpSocket. El proceso comienza con UdpSocket::bind, que asocia el socket a una dirección IP y un puerto local. Para enviar datos, se usa send_to, que requiere especificar la dirección del destinatario en cada envío. Por el contrario, recv_from es utilizado para recibir datos, devolviendo tanto la cantidad de bytes leídos como el SocketAddr del remitente. Aunque UDP es sin conexión, existe el método connect; este no establece una sesión real como en TCP, sino que simplemente fija una dirección de destino y origen por defecto, permitiendo usar los métodos más simples send y recv.
use std::net::{UdpSocket, SocketAddr};
use std::thread;
use std::time::Duration;
fn main() -> std::io::Result<()> {
// 1. Crear un socket para recibir datos en una dirección local
// El puerto :0 asigna automáticamente un puerto libre disponible
let socket_receiver = UdpSocket::bind("127.0.0.1:0")?;
let receiver_addr: SocketAddr = socket_receiver.local_addr()?;
println!("Servidor UDP escuchando en: {}", receiver_addr);
// 2. Configurar un timeout de lectura para evitar que el programa se bloquee
socket_receiver.set_read_timeout(Some(Duration::from_secs(3)))?;
// 3. Ejecutar la recepción en un hilo separado para permitir la concurrencia
let receiver_thread = thread::spawn(move || -> std::io::Result<(usize, SocketAddr)> {
let mut buf = [0u8; 1024];
// recv_from bloquea hasta recibir datos o alcanzar el timeout
// Retorna (bytes_leídos, SocketAddr_del_remitente)
let (len, src) = socket_receiver.recv_from(&mut buf)?;
println!("Hilo receptor: Recibidos {} bytes de {}", len, src);
Ok((len, src))
});
// 4. Crear un socket para enviar datos
let socket_sender = UdpSocket::bind("127.0.0.1:0")?;
let message = b"Mensaje UDP de prueba";
// 5. Enviar datagramas usando send_to hacia la dirección del receptor
println!("Enviando mensaje a {}...", receiver_addr);
socket_sender.send_to(message, receiver_addr)?;
// 6. Esperar el resultado del hilo receptor
match receiver_thread.join().unwrap() {
Ok((len, _src)) => println!("Éxito: Se procesaron {} bytes.", len),
Err(e) => eprintln!("Error en la recepción: {}", e),
}
Ok(())
}
Explicación del Código
socket_receiver: Es una instancia deUdpSocketque se encarga de escuchar la red. Se crea medianteUdpSocket::bindutilizando la dirección de bucle de retorno (127.0.0.1) y el puerto0, lo que permite al sistema operativo asignar un puerto libre.receiver_addr: De tipoSocketAddr, almacena la combinación de la dirección IP y el puerto local asignado alsocket_receiver. Es necesaria para que el emisor sepa a dónde enviar el datagrama.socket_receiver.set_read_timeout: Configura unDurationde 3 segundos. Sin esto, si no se recibe ningún paquete,recv_frombloquearía el hilo indefinidamente.buf: Un array de bytes[u8; 1024]que actúa como búfer temporal para almacenar los datos entrantes capturados porrecv_from.socket_receiver.recv_from(&mut buf): Este método es fundamental en UDP. A diferencia de TCP, donde ya existe una conexión, aquí cada paquete recibido nos entrega elsrc(la direcciónSocketAddrde quien envió el mensaje), permitiéndonos saber quién nos escribió.socket_sender: OtroUdpSocketindependiente utilizado para el envío de datos.socket_sender.send_to(message, receiver_addr): Envía el slice de bytesmessagedirectamente a la direcciónreceiver_addr. No hay validación previa de si el receptor está realmente escuchando.receiver_thread.join(): Espera a que el hilo encargado de la recepción termine su ejecución y devuelve el resultado (Okcon la tupla de bytes y dirección, oErr).
N° 93