Pregunta

Puedo crear TcpListener y TcpAcceptor con bastante facilidad en Rust, pero no entiendo muy bien lo que hacen.Parece que el oyente realmente no escucha y es solo una estructura de configuración para el aceptador.

let tcp_listener = TcpListener::bind(addr);
let mut acceptor = tcp_listener.listen();
drop(acceptor);

Una de las cosas que me desconcertó es la llamada directa.No tengo idea de dónde se define drop, busqué un poco en la biblioteca estándar pero solo puedo encontrarlo como un método de rasgo.

Además, ¿por qué dejar caer el aceptador detiene la escucha?¿No debería eso sólo impedir la aceptación?Seguramente querrás dejar al oyente.¿Por qué existe incluso un TcpListener ya que el aceptador parece hacer todo?

¿Fue útil?

Solución

En resumen, en un marco de sockets BSD, esta es la frecuencia con la que se inicia un servidor:

  • se crea un socket de servidor principal usando socket() llamada al sistema;
  • el socket principal está vinculado a una dirección/puerto específico usando bind() llamada al sistema;
  • el enchufe principal se "inicia" usando listen() llamada al sistema;desde este momento hasta que se cierre el enchufe principal, se hará cargo del puerto al que está vinculado y escuchará las conexiones entrantes;
  • cuando el enchufe principal está escuchando, es posible llamar accept();esta llamada se bloqueará hasta que algún cliente se conecte y luego devolverá un nuevo socket que representa una conexión con este cliente;toda comunicación con este cliente pasa a través de este socket;cuando finaliza la interacción, este socket debe cerrarse.

La denominación de los métodos principales utilizados por la pila TCP en Rust sigue la denominación de sockets BSD estándar; sin embargo, la API de Rust es más abstracta, por lo que algunos conceptos de bajo nivel están ocultos.

TcpListener::bind() crea un nuevo socket principal (socket() llamada al sistema) y lo vincula a la dirección proporcionada (bind() llamada al sistema).Devuelve un objeto, TcpListener, que encapsula el socket principal en estado "creado, pero no activo".

TcpListener proporciona un método llamado TcpListener::listen() que envuelve listen() llamada al sistema y "activa" el socket del servidor.Devuelve un nuevo objeto, TcpAcceptor, que proporciona una interfaz conveniente para aceptar conexiones entrantes.Representa el socket principal en estado "creado y activo".

TcpAcceptor a su vez tiene varios métodos (TcpAcceptor::incoming() y TcpAcceptor::accept()) que envuelven accept() llamada al sistema y bloquea la tarea actual hasta que algún cliente establezca una conexión que resulte en TcpStream - un objeto que representa el socket del cliente.

Todos los recursos del sistema operativo asociados con todos los sockets se liberan cuando sus destructores son llamados.Los destructores en Rust se pueden asociar con cualquier estructura personalizada.Para agregar un destructor a tu estructura tienes que implementar Drop rasgo para ello.Se llama al destructor cuando una instancia de dicha estructura sale del alcance, por ejemplo:

struct Test { value: int }

impl Drop for Test {
    fn drop(&mut self) {
        println!("Dropping Test: value is {}", self.value);
    }
}

fn main() {
    let test = Test { value: 10 };
    // main ends, `test` goes out of scope
}

Este programa imprime Dropping Test: value is 10.

¿Qué es importante para las estructuras con Drop El rasgo implementado en ellos es que no se pueden copiar implícitamente.Significa que cuando las instancias de tales estructuras se pasan a argumentos de función o se asignan a diferentes variables, se mudado desde sus ubicaciones originales, es decir, ya no podrá utilizarlos a través de sus variables originales.Por ejemplo (usando Test definición anterior):

let test = Test { value: 10 };
let test2 = test;
println!("{}", test.value);  // error: use of moved value: `test`

drop() La función que te confundió está definida en core::mem módulo; aquí es su documentación.Su implementación es muy simple;se parece a esto:

fn drop<T>(x: T) { }

¡No hace nada en absoluto!Pero debido a que acepta su argumento por valor, ese argumento se traslada al drop() llamar.Entonces dicho argumento sale inmediatamente del alcance porque la función regresa.Si T tiene un destructor asociado, se ejecutará aquí.Entonces, drop() es esencialmente un "agujero negro":todo lo que pongas en él se destruye (a menos que sea implícitamente copiable, claro está).Una propiedad muy interesante del lenguaje es que dicha función se puede escribir en Rust simple, sin ningún tipo de magia.

De hecho, drop() tiene poca utilidad porque todos los destructores siempre se ejecutan cuando los objetos correspondientes salen del alcance.En ocasiones resulta útil cuando se necesita alguna lógica compleja con recursos destructibles, pero ocurre muy raramente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top