La biblioteca de networking de Rust se encuentra en el módulo std::net. Para el Servidor.
Asociar el socket a una dirección. El método bind crea un nuevo TcpListener y lo asocia a una dirección específica.
pub fn bind<A: ToSocketAddrs>(addr: A) -> Result<TcpListener>
El listener retornado está listo para aceptar conexiones.
let listener = TcpListener::bind("127.0.0.1:80")?;
Obtener conexiones establecidas.
Sobre la estructura TcpListener se obtienen conexiones establecidas.
El método incoming retorna un iterador que devuelve una secuencia de streams de tipo TcpStream.
pub fn incoming(&self) -> Incoming<’_>
Cada stream representa una conexión abierta entre el cliente y el servidor.
for stream in listener.incoming() {
let stream = stream.unwrap();
println!("Conexion establecida!");
}
La iteración es sobre “intentos de conexiones”. Puede retornar Err.
(Forma alternativa) Obtener conexiones establecidas con accept.
El método accept obtiene una conexión establecida de un listener.
pub fn accept(&self) -> Result<(TcpStream, SocketAddr)>
El hilo se bloquea hasta que haya una conexión establecida.
match listener.accept() {
Ok((_socket, addr)) => println!("cliente:{:?}", addr),
Err(e) => println!("error: {:?}", e),
}
Leer / Escribir datos
Leer datos del socket: read.
TcpStream implementa el método read (del trait std::io::Read).
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
por ejemplo:
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
Escribir datos en el socket: write.
TcpStream implementa el método write (del trait std::io::Write).
fn write(&mut self, buf: &[u8]) -> Result<usize>
por ejemplo:
let response = "Respuesta!\\n";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
El método flush realiza una espera, previniendo que el programa continúe sin haber escrito en la conexión todos los bytes. Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
El cliente debe establecer la conexión con el servidor.
Construir la dirección de destino:
A partir de una dirección IP:
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
A partir de un nombre: método to_socket_addrs
fn to_socket_addrs(&self) -> ResultSelf::Iter
por ejemplo:
let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
Devuelve un iterador de direcciones.
Conectarse al servidor: el cliente ejecuta el método connect
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpStream>
Este método abre una conexión al host remoto.
Si se le envía un array de direcciones, intenta conectarse a cada una, hasta lograrlo.
let addrs = [
SocketAddr::from(([127, 0, 0, 1], 8080)),
SocketAddr::from(([127, 0, 0, 1], 8081)),
];
if let Ok(stream) = TcpStream::connect(&addrs[..]) {
println!("Connectado al servidor!");
} else {
println!("No se pudo conectar...");
}
Enviar y recibir datos: utiliza read y write.
El cierre de la conexión TCP puede ser realizado de forma individual.
La conexión establecida con TcpStream se cierra cuando el valor ejecuta drop.
Esto inicia el envío del mensaje close de TCP.
El método shutdown pude cerrar el extremo de escritura, de lectura o ambos.
pub fn shutdown(&self, how: Shutdown) -> Result<()>
▶ Distributed Operating Systems, Andrew S. Tanenbaum, capítulo 3 ▶ Computer Networks, Andrew S. Tanenbaum y David J. Wetherall, quinta edición ▶ The Rust Programming Language, https://doc.rust-lang.org/book/ ▶ The Complete Rust Programming Reference Guide, Rahul Sharma, Vesa Kaihlavirta, Claus Matzinger.