forked from rust-lang/async-book
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
More beginner-friendly TCP server example (rust-lang#102)
I wanted to write an example to help guide people new to asynchronous programming, with fewer external dependencies (as some people requested in #66). The example starts with the [single threaded TCP server from the Rust book](https://doc.rust-lang.org/book/ch20-01-single-threaded.html) and uses async functionality to add concurrency. The example goes through: - some examples of blocking code and how to make them non blocking (should address #64 as well) - spawning tasks vs using combinators to run them concurrently on a single thread - testing code by creating mocks and implementing async read/write traits for the mocks
- Loading branch information
Showing
27 changed files
with
597 additions
and
199 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "sync_tcp_server" | ||
version = "0.1.0" | ||
authors = ["Your Name <you@example.com"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use std::fs; | ||
use std::io::prelude::*; | ||
use std::net::TcpListener; | ||
use std::net::TcpStream; | ||
|
||
fn main() { | ||
// Listen for incoming TCP connections on localhost port 7878 | ||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); | ||
|
||
// Block forever, handling each request that arrives at this IP address | ||
for stream in listener.incoming() { | ||
let stream = stream.unwrap(); | ||
|
||
handle_connection(stream); | ||
} | ||
} | ||
|
||
fn handle_connection(mut stream: TcpStream) { | ||
// Read the first 1024 bytes of data from the stream | ||
let mut buffer = [0; 1024]; | ||
stream.read(&mut buffer).unwrap(); | ||
|
||
let get = b"GET / HTTP/1.1\r\n"; | ||
|
||
// Respond with greetings or a 404, | ||
// depending on the data in the request | ||
let (status_line, filename) = if buffer.starts_with(get) { | ||
("HTTP/1.1 200 OK\r\n\r\n", "hello.html") | ||
} else { | ||
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") | ||
}; | ||
let contents = fs::read_to_string(filename).unwrap(); | ||
|
||
// Write response back to the stream, | ||
// and flush the stream to ensure the response is sent back to the client | ||
let response = format!("{}{}", status_line, contents); | ||
stream.write(response.as_bytes()).unwrap(); | ||
stream.flush().unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "async_tcp_server" | ||
version = "0.1.0" | ||
authors = ["Your Name <you@example.com"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies.async-std] | ||
version = "1.6" | ||
features = ["attributes"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use std::net::TcpListener; | ||
use std::net::TcpStream; | ||
|
||
// ANCHOR: main_func | ||
#[async_std::main] | ||
async fn main() { | ||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); | ||
for stream in listener.incoming() { | ||
let stream = stream.unwrap(); | ||
// Warning: This is not concurrent! | ||
handle_connection(stream).await; | ||
} | ||
} | ||
// ANCHOR_END: main_func | ||
|
||
// ANCHOR: handle_connection_async | ||
async fn handle_connection(mut stream: TcpStream) { | ||
//<-- snip --> | ||
} | ||
// ANCHOR_END: handle_connection_async |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "slow_request" | ||
version = "0.1.0" | ||
authors = ["Your Name <you@example.com"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies.async-std] | ||
version = "1.6" | ||
features = ["attributes"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use std::fs; | ||
use std::io::{Read, Write}; | ||
use std::net::TcpListener; | ||
use std::net::TcpStream; | ||
use std::time::Duration; | ||
|
||
#[async_std::main] | ||
async fn main() { | ||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); | ||
for stream in listener.incoming() { | ||
let stream = stream.unwrap(); | ||
handle_connection(stream).await; | ||
} | ||
} | ||
|
||
// ANCHOR: handle_connection | ||
use async_std::task; | ||
|
||
async fn handle_connection(mut stream: TcpStream) { | ||
let mut buffer = [0; 1024]; | ||
stream.read(&mut buffer).unwrap(); | ||
|
||
let get = b"GET / HTTP/1.1\r\n"; | ||
let sleep = b"GET /sleep HTTP/1.1\r\n"; | ||
|
||
let (status_line, filename) = if buffer.starts_with(get) { | ||
("HTTP/1.1 200 OK\r\n\r\n", "hello.html") | ||
} else if buffer.starts_with(sleep) { | ||
task::sleep(Duration::from_secs(5)).await; | ||
("HTTP/1.1 200 OK\r\n\r\n", "hello.html") | ||
} else { | ||
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "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(); | ||
} | ||
// ANCHOR_END: handle_connection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "concurrent_tcp_server" | ||
version = "0.1.0" | ||
authors = ["Your Name <you@example.com"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
futures = "0.3" | ||
|
||
[dependencies.async-std] | ||
version = "1.6" | ||
features = ["attributes"] |
Oops, something went wrong.