【问题标题】:TcpStream hangs after parsing \u{d}\u{a}TcpStream 解析后挂起 \u{d}\u{a}
【发布时间】:2025-12-18 09:50:01
【问题描述】:

我正在尝试从简单的 HTTP 解析器扩展示例;

https://doc.rust-lang.org/book/ch20-01-single-threaded.html

当我解析请求时,服务器总是挂在最后一个标题行之后的行。 我为每一行添加了一些日志记录,并写出 '2' 字符 \d\a 行;

1:20:GET /data HTTP/1.1
2:22:Host: localhost:7878
3:25:User-Agent: curl/7.64.0
4:13:Accept: */*
5:20:Content-Length: 27
6:49:Content-Type: application/x-www-form-urlencoded
7:2:
\u{d}
\u{a}

为什么会这样,你可以通过运行代码然后运行来测试这个 卷曲 http://localhost:7878

你应该会看到它挂起,当你按下 Control C 时,你会看到 rust 日志显示

Natural end of HTTP request reached
We made it out!

这是出错的代码;

use std::fs;
use std::io::BufReader;
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;

enum LogLevel {
  DEBUG=0,
  NORMAL=1,
  PROD=2
}

const LOG_LEVEL : LogLevel = LogLevel::DEBUG;

const GET:i8 = 1;
const OOPS:i8 = -1;

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    log("Server started".to_string(), LogLevel::PROD);

    for stream in listener.incoming() {
        handle_connection(stream.unwrap());
    }
}

fn validate_request_verb(verb: String)->i8 {
  if verb.eq("GET") {
    return GET;
  }
  return OOPS;
}

fn handle_connection(mut stream: TcpStream) {

    let mut line_count = 0;
    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut request_verb:i8 = OOPS;

    loop {
        line_count = line_count + 1;
        let mut line = String::new();
        let line_size  = reader.read_line(&mut line).unwrap();
        if line_size > 0 {
            log(format!("{}:{}:{}", line_count, line_size, line), LogLevel::DEBUG);
            if line_count == 1 {
                let mut token_counter = 1;
                for token in line.split_whitespace(){
                    if token_counter == 1 {
                      request_verb = validate_request_verb(token.to_string());
                    }
                    token_counter = token_counter + 1;
                }
            } else { //line_count > 2
                if request_verb == GET && !line.contains(": ") {
                    //spell_out(line);
                    //TODO: this only works for GET
                    //break;

                }
            }
        } else { //Count == 0
            log("Natural end of HTTP request reached".into(), LogLevel::NORMAL);
            break;
        }
        if line_count > 100 {
            log("HTTP request had more than 100 lines".into(), LogLevel::NORMAL);
            break;
        }
    }

    println!("We made it out!");

    let contents = fs::read_to_string("hello.html").unwrap();

    let response = format!(
        "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
        contents.len(),
        contents
    );

    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

fn log(message: String , maximum_log_level:LogLevel){
  if LOG_LEVEL as i8 <= maximum_log_level as i8 {
      if message.ends_with("\n") {
          print!("{}", message);
      } else {
          println!("{}", message);
      }
  }
}

fn spell_out(s: String){
    for c in s.chars(){
        println!("{}", c.escape_unicode());
    }
}

最后一点,根据

Reading from a TcpStream with Read::read_to_string hangs until the connection is closed by the remote end

这应该通过使用read_to_string 来解决,但没有运气..

【问题讨论】:

    标签: rust tcpserver


    【解决方案1】:

    查看read_line 的文档。它返回读取的字节数,其中包括换行符本身。所以它永远不会是0,除非流被关闭,因此你的else { // Count == 0 将无法自然地跳出循环。

    【讨论】:

    • 我在家时会完全更新问题,但是当我发帖时,还有更多数据需要处理,但它仍然会挂在那里。另外,我期待 EOF 在tpcstream的结束,不现实吗?
    • 在您发送响应之前,tcp 连接不会关闭,因此在读取时等待 EOF 可能不起作用。