2014-09-03 16 views
6

Wydaje mi się, że borykam się ze std :: io :: TcpStream. Właściwie to próbuję otworzyć połączenie TCP z innym systemem, ale poniższy kod dokładnie emuluje problem.Rust persistent TcpStream

Mam serwer Tcp, który po otwarciu pisze po prostu "Hello World" do TcpStream, a następnie pętle, aby pozostawić połączenie otwarte.

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 {} 
} 

Cała klient robi, to próbuje odczytać jeden bajt z połączenia i wydrukować go.

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 
     } 
     } 
    } 
} 

Problem polega na tym, że mój klient pozostaje zablokowany podczas odczytu, dopóki nie zabiję serwera lub nie zamknę połączenia TCP. To nie jest to, czego chcę, muszę otworzyć połączenie TCP przez bardzo długi czas i wysyłać wiadomości pomiędzy klientem a serwerem. Co ja tu nie rozumiem? Mam taki sam problem z rzeczywistym systemem, z którym się komunikuję - odblokowuję się, gdy zabiję połączenie.

Odpowiedz

7

Niestety, Rust nie ma obecnie żadnych urządzeń do asynchronicznego I/O. Tam są pewne próby naprawienia sytuacji, ale są one jeszcze bliskie ukończenia. Oznacza to, że istnieje chęć umożliwienia prawdziwie asynchronicznego wejścia/wyjścia (propozycje obejmują wybór źródeł I/O i kanałów w tym samym czasie, co pozwoliłoby na budzenie zadań, które są blokowane w operacji wejścia/wyjścia przez zdarzenie na kanał, chociaż nie jest jasne, w jaki sposób powinno to zostać wdrożone na wszystkich obsługiwanych platformach), ale wciąż pozostaje wiele do zrobienia i nie ma nic użytecznego teraz, o ile wiem.

Możesz jednak naśladować to w pewnym stopniu dzięki limitom czasu. To dalekie od najlepszego rozwiązania, ale działa. To może wyglądać następująco (uproszczony przykład z mojego kodu bazowego):

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 
} 

Tutaj recv_from powróci IoError z kind zestaw do TimedOut jeśli nie ma dostępnych danych na gnieździe w ciągu 5 sekund wewnątrz recv_from rozmowy. Musisz zresetować limit czasu przed rozpoczęciem każdej iteracji pętli, ponieważ jest to bardziej "termin" niż limit czasu - kiedy wygasa, wszystkie połączenia zaczynają się z błędem limitu czasu.

To zdecydowanie nie jest sposób, w jaki należy to zrobić, ale Rust obecnie nie zapewnia niczego lepszego. Przynajmniej to działa.

Aktualizacja

Obecnie próbą utworzenia asynchronicznej pętli zdarzenia i sieci I/O w oparciu o niego. Nazywa się mio. Prawdopodobnie może to być dobre tymczasowe (lub nawet trwałe, kto wie) rozwiązanie dla asynchronicznych operacji we/wy.

+0

OK, więc nie będę w stanie wykonać prawidłowego asynchronicznego I/O ... bummer. Ale dlaczego nie mogę odczytać danych z TcpStream, dopóki połączenie nie zostanie zamknięte? Czy jest zaimplementowany do buforowania wszystkich danych do EOF? – Upio

+0

@Upio, czy nie powiedziałeś tego: "Problem polega na tym, że mój klient pozostaje zablokowany podczas odczytu, dopóki nie zabiję serwera lub nie zamknę połączenia TCP"? Jest to "czytanie danych z TcpStream do czasu zakończenia połączenia", nieprawdaż? W każdym razie 'TcpStream' jest [' Reader'] (http://doc.rust-lang.org/std/io/trait.Reader.html), więc ma ['read_to_end()'] (http://doc.rust-lang.org/std/io/trait.Reader.html#method.read_to_end) metoda, która zużywa cały strumień, dopóki nie zostanie zamknięty. Czy to jest to, czego potrzebujesz? –

+0

Przepraszam, jeśli byłem niejasny, już wiem, jak wchodzić w interakcję z czytelnikiem. Chciałbym tylko zrozumieć, dlaczego żadne dane nie są odczytywane po stronie klienta, dopóki połączenie się nie zamknie. Nawet ja używam read_byte(), blokuje się dopóki połączenie się nie zakończy.Jak to działa wewnętrznie? – Upio