TcpStream.read阻塞,如何解决呢

我是rust新手,最近在学习网络模块,遇到了这个阻塞问题。我看别人的代码都是直接调用read()方法,但是我的代码会被阻塞。现象如下:

  • 运行程序
  • 浏览器输入127.0.0.1:4221
  • 控制台无输入,浏览器一直在转圈

最后发现是_stream.read() 再次进入时会阻塞,我想应该是EOF, 但是如何处理EOF呢,它也不报错,也不返回OK(0)?
代码如下:

// Uncomment this block to pass the first stage
use std::net::TcpListener;
use std::io::{ErrorKind, Read, Write};


fn main(){
    // You can use print statements as follows for debugging, they'll be visible when running tests.
    println!("Logs from your program will appear here!");
    // Uncomment this block to pass the first stage
     let listener = TcpListener::bind("127.0.0.1:4221").unwrap();
     //listener.set_nonblocking(true).unwrap();
    //
    for stream in listener.incoming() {
         match stream {
            Ok(mut _stream) => {
                println!("accepted new connection");
                let mut data = String::new();
                let mut buffer = [0; 2048];
                loop {
                    _stream.set_ttl(110).unwrap();
                    let result = _stream.read(&mut buffer);
                    match result {
                        Ok(len)=>{
                            if len==0 {
                                println!("len==0, have read all data from stream");
                                break;
                            }
                            data.push_str(std::str::from_utf8(&buffer).unwrap())
                        },
                        Err(e)=>{
                            if e.kind() == ErrorKind::UnexpectedEof {
                                println!("EOF, have read all data from stream");
                                break;
                            }
                        }
                    }
                }
                if data.is_empty() {
                    println!("receive nothing ", );
                }else {
                    //let  content = String::from_utf8(all_datas).expect("error when converts String");
                    println!("the request content is {}", data);

                    let path = data.split_whitespace().nth(1).unwrap();
                    println!("the request path is {}", path);
                    let response = if "/"==path {
                        "HTTP/1.1 200 \r\n\r\n"
                    }else {
                        "HTTP/1.1 404  Not Found\r\n\r\n"
                    };
                    _stream.write(response.as_bytes()).expect("Response to client failed!");
                    _stream.flush().expect("Some errors occurs when flush");
                }
            }
            Err(e) => {
                println!("error: {}", e);
         }
       }
     }
}

TcpStream.read阻塞,如何解决呢

最佳答案

data.push_str(std::str::from_utf8(&buffer).unwrap()) 这个后面你得判断你的http协议本次是不是读完整了 读完整了就break掉

8个月前 评论
讨论数量: 4

data.push_str(std::str::from_utf8(&buffer).unwrap()) 这个后面你得判断你的http协议本次是不是读完整了 读完整了就break掉

8个月前 评论
长日将尽

read 阻塞是因为没有数据可读,浏览器访问该服务器的时候并没有向服务器写入请求数据。如果用 TcpStream 再写一个客户端,向服务器写入一些数据,这个例子应该就能正常运行了。

8个月前 评论

虽然没有数据,但是浏览器请求路径没有错,不应该阻塞,而是应该返回一个空的response, 这个是理想效果。

8个月前 评论

我参考网上的例子,判断所有内容读取完了就跳出循环:

// Uncomment this block to pass the first stage
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;


struct Request {
    method          :   String,
    http_version    :   String,
    ip              :   String,
    port            :   u32,
    path            :   String,
    user_agent      :   String,
}

fn if_data_read_end(buf: &Vec<u8>) ->bool {
    let len = buf.len();
    return if len <=4 {
        false
    }else {
        //because it always is the '\r\n\r\n' to end the request.
        buf[len-1]==b'\n' && buf[len-2]==b'\r' && buf[len-3]==b'\n' && buf[len-4]==b'\r'
    };
}


fn parse_request_header(mut stream: &TcpStream) ->Request {
    let get   ="get";
    let post  = "post";
    let mut ip:         String = "".to_string();
    let mut port:       u32 = 80;
    let mut method:     String = "".to_string();
    let mut http_version:    String = "".to_string();
    let mut path:           String = "".to_string();
    let mut user_agent:     String ="".to_string();

    println!("accepted new connection");
    let mut data = vec![0u8,0];
    let mut buffer = [0; 256];
    loop {
        if if_data_read_end(&data) {
            break;
        }
        let length = stream.read(&mut buffer).unwrap();
        if length==0 {
            println!("len==0, have read all data from stream");
            break;
        }else {
            data.append(&mut buffer[..length].to_vec());
        }
    };
    let request = String::from_utf8(data).unwrap();
    let v: Vec<&str> = request.lines().map(|line|line).collect();
    for item in v {
        if !item.is_empty()  {
            if item.to_lowercase().contains(get) {
                method = get.to_string().to_uppercase();
            }else if item.to_lowercase().contains(post) {
                method = post.to_string().to_uppercase();
            }
            if item.contains("HTTP") {
                let version = item.split_whitespace().nth(2);
                http_version = version.unwrap().to_string();
                path = item.split_whitespace().nth(1).unwrap_or("/").to_string();
            }
            if item.contains("Host") {
                ip = item.split(":").nth(1).unwrap().to_string();
                let port_str = item.split(":").nth(2).unwrap();
                port = port_str.parse().unwrap_or(0);
            }
            if item.contains("User-Agent") {

                user_agent = item.split_once(":").unwrap().1.trim().to_string();
            }

        }
    }

    Request {
        method,
        http_version,
        ip,
        port,
        path,
        user_agent,
    }
}

fn pre_handle_path(mut path: String, mut req: Request) -> Request {
    if path.is_empty() {
        req.path = "/".to_string();
    }else {
        req.path = path.trim().to_string();
    }
    req
}
fn dispatch(req: Request, stream: TcpStream) {
    let path = req.path;
    let resp_content=
    if path == "/" {
        "HTTP/1.1 200 \r\n\r\n".to_string()
    } else if path.starts_with("/echo/") {
        let val = path.strip_prefix("/echo/").unwrap();
        format!(
            "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\r\n{}\r\n",
            val.len(),
            val
        )
    }else if path.starts_with("/user-agent") {
        format!(
            "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\r\n{}\r\n",
            req.user_agent.len(),
            req.user_agent
        )
    }else {
         "HTTP/1.1 404  Not Found\r\n\r\n".to_string()
    };
    println!("{}", resp_content);
    response(stream, resp_content.as_str());
}

fn response(mut stream:TcpStream, resp:&str){
    stream.write_all(resp.as_bytes()).expect("Response to client failed!");
    stream.flush().expect("Some errors occurs when flush");
}

fn main(){
    // You can use print statements as follows for debugging, they'll be visible when running tests.
    println!("Logs from your program will appear here!");
    // Uncomment this block to pass the first stage
     let listener = TcpListener::bind("127.0.0.1:4221").unwrap();
     //listener.set_nonblocking(true).unwrap();
    //dispatch
    for stream in listener.incoming() {
         match stream {
            Ok(mut _stream) => {
                thread::spawn(move ||{
                    let req = parse_request_header(&_stream);
                    let req1 = pre_handle_path(req.path.clone(), req);
                    dispatch(req1, _stream);
                });

            }
            Err(e) => {
                println!("error: {}", e);
         }
       }
     }
}

现在是可以的,不会阻塞,即使是request body 是空的请求。

8个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!