2014-09-03 7 views
6

Mi sembra che stia lottando con lo std :: io :: TcpStream. In realtà sto provando ad aprire una connessione TCP con un altro sistema, ma il codice seguente emula esattamente il problema.Rust persistent TcpStream

Ho un server Tcp che scrive semplicemente "Hello World" sul TcpStream all'apertura e quindi su loop per mantenere aperta la connessione.

fn main() { 
    let listener = io::TcpListener::bind("127.0.0.1", 8080); 
    let mut acceptor = listener.listen(); 
    for stream in acceptor.incoming() { 

    match stream { 
     Err(_) => { /* connection failed */ } 
     Ok(stream) => spawn(proc() { 
     handle(stream); 
     }) 
    } 
    } 
    drop(acceptor); 
} 

fn handle(mut stream: io::TcpStream) { 
    stream.write(b"Hello Connection"); 
    loop {} 
} 

Tutto il client tenta di leggere un singolo byte dalla connessione e stamparlo.

fn main() { 
    let mut socket = io::TcpStream::connect("127.0.0.1", 8080).unwrap(); 
    loop { 
     match socket.read_byte() { 
     Ok(i) => print!("{}", i), 
     Err(e) => { 
      println!("Error: {}", e); 
      break 
     } 
     } 
    } 
} 

Ora il problema è il mio cliente rimane bloccato sulla lettura fino a quando uccido il server o chiudere la connessione TCP. Questo non è quello che voglio, ho bisogno di aprire una connessione TCP per un tempo molto lungo e inviare messaggi avanti e indietro tra client e server. Che cosa sto fraintendendo qui? Ho lo stesso identico problema con il sistema reale con cui sto comunicando - Divento sbloccato solo quando avrò terminato la connessione.

risposta

7

Sfortunatamente, Rust non ha alcuna funzione per l'I/O asincrono ora. Ci sono are alcuni tentativi di rettificare la situazione, ma sono ancora lontani dall'essere completi. Cioè, vi è il desiderio di rendere possibile l'I/O realmente asincrono (le proposte includono la selezione su fonti I/O e canali allo stesso tempo, il che consentirebbe attività di risveglio che sono bloccate all'interno di un'operazione I/O tramite un evento su un canale, anche se non è chiaro come questo dovrebbe essere implementato su tutte le piattaforme supportate), ma c'è ancora molto da fare e non c'è nulla di realmente utilizzabile ora, per quanto ne so.

Tuttavia, è possibile emularlo in qualche modo con i timeout. Questo è lontano dalla soluzione migliore, ma funziona. Potrebbe assomigliare a questo (esempio semplificato dal mio codice di base):

let mut socket = UdpSocket::bind(address).unwrap(); 

let mut buf = [0u8, ..MAX_BUF_LEN]; 
loop { 
    socket.set_read_timeout(Some(5000)); 
    match socket.recv_from(buf) { 
     Ok((amt, src)) => { /* handle successful read */ } 
     Err(ref e) if e.kind == TimedOut => {} // continue 
     Err(e) => fail!("error receiving data: {}", e) // bail out 
    } 

    // do other work, check exit flags, for example 
} 

Qui recv_from sarà tornare IoError con kind set per TimedOut se non ci sono dati disponibili sul socket per 5 secondi all'interno recv_from chiamata. È necessario reimpostare il timeout prima di ogni iterazione del ciclo poiché è più simile a una "scadenza" che un timeout: quando scade, tutte le chiamate inizieranno a fallire con un errore di timeout.

Questo non è sicuramente il modo in cui dovrebbe essere fatto, ma attualmente Rust non fornisce nulla di meglio. Almeno fa il suo lavoro.

Aggiornamento

Ora c'è un tentativo di creare un ciclo di eventi asincroni e la rete di I/O basati su di esso. Si chiama mio. Probabilmente può essere una buona soluzione temporanea (o addirittura permanente, chissà) per l'I/O asincrono.

+0

OK, quindi non sarò in grado di fare l'I/O asincrono corretto ... insopportabile. Ma perché non posso leggere i dati dal TcpStream fino a quando la connessione non è stata chiusa? È implementato per bufferizzare tutti i dati fino all'EOF? – Upio

+0

@Upio, non hai detto questo: "Ora il problema è che il mio client rimane bloccato sulla lettura fino a quando non uccido il server o chiudo la connessione TCP"? Questo è "leggere i dati dal TcpStream fino a quando la connessione non è chiusa", non è vero? Ad ogni modo, 'TcpStream' è un [' Reader'] (http://doc.rust-lang.org/std/io/trait.Reader.html), quindi ha ['read_to_end()'] (http://doc.rust-lang.org/std/io/trait.Reader.html#method.read_to_end) metodo che consuma tutto lo stream fino a quando non viene chiuso. E 'quello di cui hai bisogno? –

+0

Scusate se non sono chiaro, so già come interagire con il lettore. Vorrei solo capire perché nessun dato viene letto dal lato client fino alla chiusura della connessione. Anche io uso read_byte(), blocca fino a quando la connessione termina.Come funziona internamente? – Upio