zoukankan      html  css  js  c++  java
  • [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器]

    项目实战

    实战3:Http服务器

    我们今天来开发我们的Http服务器。

    我们先用命令创建一个工程目录:

    cargo new h_server
    

    我们现在开始在src/main.rs写如下代码:

    use std::net::TcpListener;
    
    fn main() {
        //绑定IP和端口
        let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
         println!("server is up!");
    	//从监听器得到网络IO流
        for stream in listener.incoming() {
            let stream = stream.unwrap();
    
            println!("Connection established!");
        }
    }
    

    然后我们直接启动我们的服务器,用命令跑一下:

    cargo run 
    

    控制台打印:

     Finished dev [unoptimized + debuginfo] target(s) in 0.87s
         Running `targetdebugh_server.exe`
    server is up!
    

    说明服务器已经启动。

    我们现在直接用浏览器访问:http://127.0.0.1:7878/

    然后,我们回到vscode的控制台,会打印如下结果:

    ......
    Connection established!
    Connection established!
    Connection established!
    

    好的,我们简单的服务器开发好了!哈哈!

    这里为什么会打印三次呢?

    因为浏览器跟我们的服务器通讯的时候,可能各种原因,会请求重试几次。

    所以,服务器有可能会收到多次的请求。

    先不管这些细节,总之我们已经成功地连接上我们的服务器。

    这时,我们直接用:CTRL+c,直接退出服务器。

    那浏览器发过来的请求信息,究竟是什么样的呢?

    我们来加一些代码,如下:

    use std::io::{Read, Write};
    use std::net::{Shutdown, TcpListener, TcpStream};
    use std::thread;
    fn main() {
        let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); //绑定监听端口
        println!("server is up!");
        for stream in listener.incoming() {
            let stream = stream.unwrap();
            println!("Connection established!");
            handle_connection(stream);
        }
    }
    //处理连接业务逻辑
    fn handle_connection(mut stream: TcpStream) {
        let mut buffer = [0; 512]; //用一个长度为512的buffer
    
        stream.read(&mut buffer).unwrap(); //从Stream流中读取数据
    
        println!("Request: {}", String::from_utf8_lossy(&buffer[..])); //把buffer中的流数据转化成字符串
    }
    
    

    同样运行命令:

    cargo run 
    

    浏览器访问:

    服务器打印结果为:

    Connection established!
    Request: GET / HTTP/1.1
    Host: 127.0.0.1:7878
    Connection: keep-alive
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
    Sec-Fetch-User: ?1
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0
    Connection established!
    Request: GET / HTTP/1.1
    Host: 127.0.0.1:7878
    Connection: keep-alive
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
    Sec-Fetch-User: ?1
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0
    Connection established!
    Request: GET / HTTP/1.1
    Host: 127.0.0.1:7878
    Connection: keep-alive
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
    Sec-Fetch-User: ?1
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0
    

    我会发现,这里浏览器发过来的信息,是一些Key:Value键值对的信息。

    这些信息,其实就代表http请求协议。

    简单来说,http协议,就是浏览器与Http服务器对话的通讯标准,就像我们跟中国人用中文交流,跟美国人,用英语交流一样。

    不同的协议,就像不同的语言。

    关于http协议,请看:关于HTTP协议,一篇就够了

    HTTP协议详解(真的很经典)

    这里不再说明 。

    我们再来增加一些代码,用来响应http的请求。

    我们这次直接返回html页面。

    我们在工程目录下新建一个html文件:hello.html ,其内容如下:

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

    main.rs代码中函数handle_connection,更新为如下代码:

    //处理连接业务逻辑
    fn handle_connection(mut stream: TcpStream) {
        let mut buffer = [0; 512]; //用一个长度为512的buffer
    
        stream.read(&mut buffer).unwrap(); //从Stream流中读取数据
    
        println!("Request: {}", String::from_utf8_lossy(&buffer[..])); //把buffer中的流数据转化成字符串
        let contents = fs::read_to_string("hello.html").unwrap();
    
        let response = format!("HTTP/1.1 200 OK
    
    {}", contents);
    
        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }
    
    

    我们再次用命令运行:

    cargo run 
    

    启动服务器。

    然后用浏览器访问:http://127.0.0.1:7878/

    我们将得到一个页面 ,里面内容显示:

    Hello!
    
    Hi from Rust
    

    很好。

    我们现在来增加对浏览器请求的检查处理逻辑,handle_connection函数代码更新如下:

    fn handle_connection(mut stream: TcpStream) {
        let mut buffer = [0; 512];
        stream.read(&mut buffer).unwrap();
    
        let get = b"GET / HTTP/1.1
    ";
    
        if buffer.starts_with(get) {
            let contents = fs::read_to_string("hello.html").unwrap();
    
            let response = format!("HTTP/1.1 200 OK
    
    {}", contents);
    
            stream.write(response.as_bytes()).unwrap();
            stream.flush().unwrap();
        } else {
            // some other request
        }
    }
    

    我们再次用命令运行:

    cargo run 
    

    启动服务器。

    然后用浏览器访问:http://127.0.0.1:7878/

    内容跟上次一样,但我们现在用:http://127.0.0.1:7878/a

    结果显示为错误。

    因为我们没有处理错误情况。

    好吧,我们现在来处理一下。

    在当前工程目录下,创建一个html文件:404.html,整个目录结构如下:

    |-Cargo.toml
    |-404.html
    |-hello.html
    |-src
        |- main.rs
    |-tests
        |- test.rs
    

    src/main.rs的函数handle_connection代码更新如下:

    fn handle_connection(mut stream: TcpStream) {
        let mut buffer = [0; 512];
        stream.read(&mut buffer).unwrap();
    
        let get = b"GET / HTTP/1.1
    ";
        // --snip--
    
        let (status_line, filename) = if buffer.starts_with(get) {
            ("HTTP/1.1 200 OK
    
    ", "hello.html")
        } else {
            ("HTTP/1.1 404 NOT FOUND
    
    ", "404.html")
        };
    
        let contents = fs::read_to_string(filename).unwrap();
    
        let response = format!("{}{}", status_line, contents);
    
        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }
    

    我们再次用命令运行:

    cargo run 
    

    启动服务器。

    然后用浏览器访问:http://127.0.0.1:7878/

    内容跟上次一样,但我们现在用:http://127.0.0.1:7878/a

    结果显示:

    Oops!
    
    Sorry, I don't know what you're asking for.
    

    很好!符合我们的预期。

    我们现在已经打造好一个简单版本的http服务器。

    以上,希望对你有用。

    如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
    

    参考文章:

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

    https://riptutorial.com/rust/example/4404/a-simple-tcp-client-and-server-application--echo

  • 相关阅读:
    01、MySQL_简介
    算法—打擂台法
    第10章 对文件的输入输出
    第9章 用户自己建立数据类型
    Spring Cloud
    JUC
    Swagger Learing
    JUC
    Spring Data
    SpringCloud
  • 原文地址:https://www.cnblogs.com/gyc567/p/12074343.html
Copyright © 2011-2022 走看看