rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.31k stars 12.58k forks source link

TcpStream with non_blocking mode and read_to_end() leads to incomplete message #75973

Open JakkuSakura opened 4 years ago

JakkuSakura commented 4 years ago

I ran in to this problem:

I have a TcpStream set on non_blocking(true). When I read from it, depending on the delay after I send the http request, it may drop some data of the reponse or not. It shouldn't work like this anyway.

Moreover, it seems like that read_to_end will end when meeting EOF. How will this behavior in non_blocking mode? Is there a way to tell how many bytes is ready without actually reading in order to pre-allocate buffers?

When the delay is less than 30 ms

len: 183
);};}})();};if(window.pageState==0){initIndex();}})();document.cookie = 'IS_STATIC=1;expires=' + new Date(new Date().getTime() + 10*60*1000).toGMTString();</script>
</body></html>

When the delay is more than 30 ms, say 100 ms

len: 15550
 HTTP/1.0 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-Length: 14615
Content-Type: text/html
<more>

Code

use std::io::{Write, Read};
use std::net::TcpStream;
use std::{str, thread};
use std::time::Duration;

#[allow(dead_code)]
fn sleep(ms: u64) {
    thread::sleep(Duration::from_millis(ms));
}

fn main() {
    // www.baidu.com
    let mut tcp = TcpStream::connect("39.156.66.14:80").unwrap();
    tcp.set_nonblocking(true).unwrap();
    tcp.set_nodelay(true).unwrap();
    tcp.write(b"GET / HTTP/1.0\r\n\r\n").unwrap();
    // FIXME if sleep time is less than 30, it will receive incomplete message
    // sleep(0);
    sleep(100);
    loop {
        tcp.flush().unwrap();
        let mut v = vec![];
        // FIXME does read to end make sense for non blocking?
        match tcp.read_to_end(&mut v) {
            Ok(n) => {
                if v.len() > 0 {
                    unsafe {
                        println!("len: {}\n {}", n, str::from_utf8_unchecked(&v));
                    }
                    break;
                }
            }
            Err(_e) => {
                print!(".");
            }
        }
    }
}
JakkuSakura commented 4 years ago

I tested read_to_end(), and found out if I use read_to_end(), I will encounter the problem. Maybe there is a flaw in the implementation of read_to_end()