< 返回版块

Latias94 发表于 2022-02-21 23:16

Tags:futures

书章节链接:Final Project: Building a Concurrent Web Server with Async Rust

书中对原本的服务器代码,用了 async-std 来改造,确保 handle_connection 是非阻塞的,其中书里用了 sleep() 来模拟一个慢请求。

但是我按照书里改造了下,开两个网页同时访问 http://127.0.0.1:7878/sleep ,依旧是第一个网页加载了5秒后,第二个网页也加载了5秒(共10秒),因此疑惑是不是代码哪里实现错了,来论坛求解答。

use async_std::io::{Read, Write};
use async_std::net::TcpListener;
use async_std::task::spawn;
use futures::stream::StreamExt;
use futures::{AsyncReadExt, AsyncWriteExt};
use async_std::task;

use std::time::Duration;

const HTML_404: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf - 8">
    <title>Hello!</title>
</head>
<body>
<h1>Oops!</h1>
<p>Sorry, I don't know what you're asking for.</p>
</body>
</html>"#;

const HTML_HELLO: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Hello!</title>
</head>
<body>
<h1>Hello!</h1>
<p>Hi from Rust</p>
</body>
</html>"#;

#[async_std::main]
async fn main() {
    // 监听本地端口 7878 ,等待 TCP 连接的建立
    let listener = TcpListener::bind("127.0.0.1:7878").await.unwrap();

    // std::net::TcpListener 的 listener.incoming() 是阻塞的迭代器
    // for stream in listener.incoming() {
    //     let stream = stream.unwrap();
    //
    //     handle_connection(stream).await;
    // }

    // 异步版本的 TcpListener 为 listener.incoming() 实现了 Stream 特征
    // 1. listener.incoming() 不再阻塞
    // 2. 使用 for_each_concurrent 并发地处理从 Stream 获取的元素
    // 至此,我们实现了同时使用并行(多线程)和并发( async )来同时处理多个请求!
    listener
        .incoming()
        .for_each_concurrent(/* limit */ None, |tcpstream| async move {
            let tcpstream = tcpstream.unwrap();
            spawn(handle_connection(tcpstream));
        })
        .await;
}

async fn handle_connection(mut stream: impl Read + Write + Unpin) {
    // 从连接中顺序读取 1024 字节数据
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).await.unwrap();

    let get = b"GET / HTTP/1.1\r\n";
    let sleep = b"GET /sleep HTTP/1.1\r\n";

    // 处理HTTP协议头,若不符合则返回404和对应的`html`文件
    let (status_line, contents) = if buffer.starts_with(get) {
        ("HTTP/1.1 200 OK\r\n\r\n", HTML_HELLO)
    } else if buffer.starts_with(sleep) {
        task::sleep(Duration::from_secs(5)).await;
        ("HTTP/1.1 200 OK\r\n\r\n", HTML_HELLO)
    } else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n", HTML_404)
    };

    // 将回复内容写入连接缓存中
    let response = format!("{status_line}{contents}");
    stream.write(response.as_bytes()).await.unwrap();
    // 使用flush将缓存中的内容发送到客户端
    stream.flush().await.unwrap();
}
[package]
name = "asynchronous"
version = "0.1.0"
edition = "2021"

[dependencies]
futures = "0.3"

Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5b1bce4feecdda03bf0ff4965acd2cbe

评论区

写评论
作者 Latias94 2022-02-22 10:58

解决了!一个用浏览器,一个用隐私模式打开就没问题了!!!

--
👇
fakeshadow: 建议用其他工具测试. 浏览器默认会复用单连接

fakeshadow 2022-02-22 10:53

建议用其他工具测试. 浏览器默认会复用单连接

作者 Latias94 2022-02-22 10:08

问题就是这个改造后的代码也有同样的问题,第二个链接在10秒之后才响应🤦‍♂️

👇
Grobycn: 听起来没问题啊。 同一时间打开两个链接,5秒钟之后两个链接都返回响应了。 阻塞的话,同时打开,其中一个会在10秒之后响应。

Grobycn 2022-02-21 23:47

听起来没问题啊。 同一时间打开两个链接,5秒钟之后两个链接都返回响应了。 阻塞的话,同时打开,其中一个会在10秒之后响应。

1 共 4 条评论, 1 页