From 3fd8485c1c1919b32bb6949031b0baf1b333bf10 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Wed, 23 Aug 2023 17:37:48 +0800 Subject: [PATCH] std: add example apps for tokio and socket2 --- Cargo.lock | 122 ++++++++++++++++++++++++ Cargo.toml | 4 + apps/std/socket2-echoserver/Cargo.toml | 14 +++ apps/std/socket2-echoserver/src/main.rs | 67 +++++++++++++ apps/std/tokio-echo/Cargo.toml | 13 +++ apps/std/tokio-echo/src/main.rs | 78 +++++++++++++++ 6 files changed, 298 insertions(+) create mode 100644 apps/std/socket2-echoserver/Cargo.toml create mode 100644 apps/std/socket2-echoserver/src/main.rs create mode 100644 apps/std/tokio-echo/Cargo.toml create mode 100644 apps/std/tokio-echo/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ee48eb4248..bd9cf823fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "tock-registers", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.0.4" @@ -509,6 +524,21 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -605,6 +635,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "capability" version = "0.1.0" @@ -1080,6 +1116,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "glob" version = "0.3.1" @@ -1347,6 +1389,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.8" @@ -1402,6 +1453,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -1471,6 +1531,12 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "pin-project-lite" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" + [[package]] name = "plotters" version = "0.3.5" @@ -1692,6 +1758,12 @@ dependencies = [ "svgbobdoc", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1836,6 +1908,15 @@ dependencies = [ "managed", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "git+https://github.com/arceos-os/socket2.git?branch=arceos#70dc3e9ecccfa5bbd7e9aa028b4b4c30f42f1757" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.5.2" @@ -1886,6 +1967,21 @@ dependencies = [ "mio", ] +[[package]] +name = "std-socket2-echoserver" +version = "0.1.0" +dependencies = [ + "env_logger", + "socket2", +] + +[[package]] +name = "std-tokio-echo" +version = "0.1.0" +dependencies = [ + "tokio", +] + [[package]] name = "strsim" version = "0.10.0" @@ -2006,6 +2102,32 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" +[[package]] +name = "tokio" +version = "1.29.1" +source = "git+https://github.com/arceos-os/tokio.git?branch=arceos#6535838780eb33bf03988cc62fa01df7551505f4" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "git+https://github.com/arceos-os/tokio.git?branch=arceos#6535838780eb33bf03988cc62fa01df7551505f4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "toml" version = "0.5.11" diff --git a/Cargo.toml b/Cargo.toml index 40bcf56f3e..eef0f86c5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,8 @@ members = [ "apps/std/mio-httpserver", "apps/std/mio-tcpserver", + "apps/std/tokio-echo", + "apps/std/socket2-echoserver", ] [profile.release] @@ -80,3 +82,5 @@ lto = true [patch.crates-io] crate_interface = { path = "crates/crate_interface" } mio = { git = "https://github.com/arceos-os/mio.git", branch = "arceos" } +tokio = { git = "https://github.com/arceos-os/tokio.git", branch = "arceos" } +socket2 = { git = "https://github.com/arceos-os/socket2.git", branch = "arceos" } diff --git a/apps/std/socket2-echoserver/Cargo.toml b/apps/std/socket2-echoserver/Cargo.toml new file mode 100644 index 0000000000..b4408ed7eb --- /dev/null +++ b/apps/std/socket2-echoserver/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "std-socket2-echoserver" +version = "0.1.0" +edition = "2021" +authors = ["scpointer "] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +env_logger = { version = "0.9.3", default-features = false } +socket2 = {version = "0.4" } + +[package.metadata.arceos] +features = ["net"] diff --git a/apps/std/socket2-echoserver/src/main.rs b/apps/std/socket2-echoserver/src/main.rs new file mode 100644 index 0000000000..100a8b5997 --- /dev/null +++ b/apps/std/socket2-echoserver/src/main.rs @@ -0,0 +1,67 @@ +//! Configuration needed to run this test (in the root Makefile): +//! A=apps/std/socket2-echoserver STD=y NET=y +use socket2::{Domain, Socket, Type}; +use std::io::Write; +use std::mem::MaybeUninit; +use std::net::SocketAddr; +use std::str::from_utf8; + +const DATA: &[u8] = b"hello world"; +const BUF_SIZE: usize = 4096; +const ADDR: &str = "0.0.0.0:5555"; + +fn main() { + env_logger::init(); + test() +} + +fn test() { + let mut recv_buffer = [MaybeUninit::::new(0); BUF_SIZE]; + let mut send_buffer = [0u8; BUF_SIZE]; + + let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); + let addr: SocketAddr = ADDR.parse::().unwrap(); + println!("addr {:#?}", addr); + #[cfg(not(target_os = "arceos"))] + let addr: socket2::SockAddr = addr.into(); + socket.bind(&addr).unwrap(); + socket.listen(128).unwrap(); + + println!("---------- socket2 echoserver ----------"); + println!("type `nc {}` at another terminal:", ADDR); + + loop { + let (mut connection, sockaddr) = loop { + if let Ok(result) = socket.accept() { + break result; + } else { + println!("user got a Err from accept, try again"); + } + }; + #[cfg(target_os = "arceos")] + println!("Accepted connection from: {}", sockaddr); + #[cfg(not(target_os = "arceos"))] + println!( + "Accepted connection from: {}", + sockaddr.as_socket().unwrap() + ); + connection.write_all(DATA).unwrap(); + + loop { + let n = connection.recv(&mut recv_buffer).unwrap(); + if n == 0 { + break; + } + for i in 0..n { + send_buffer[i] = unsafe { recv_buffer[i].assume_init() }; + } + let received_data = &send_buffer[..n]; + if let Ok(str_buf) = from_utf8(received_data) { + println!("Received data({}B): {}", n, str_buf.trim_end()); + connection.write_all(received_data).unwrap(); + } else { + println!("Received (none UTF-8) data: {:?}", received_data); + } + } + } +} diff --git a/apps/std/tokio-echo/Cargo.toml b/apps/std/tokio-echo/Cargo.toml new file mode 100644 index 0000000000..8292a123bd --- /dev/null +++ b/apps/std/tokio-echo/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "std-tokio-echo" +version = "0.1.0" +edition = "2021" +authors = ["Yuekai Jia "] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.29", features = ["macros", "rt", "io-util", "net"] } + +[package.metadata.arceos] +features = ["net", "multitask"] diff --git a/apps/std/tokio-echo/src/main.rs b/apps/std/tokio-echo/src/main.rs new file mode 100644 index 0000000000..102989dfc3 --- /dev/null +++ b/apps/std/tokio-echo/src/main.rs @@ -0,0 +1,78 @@ +//! A "hello world" echo server with Tokio +//! +//! This server will create a TCP listener, accept connections in a loop, and +//! write back everything that's read off of each TCP connection. +//! +//! Because the Tokio runtime uses a thread pool, each TCP connection is +//! processed concurrently with all other TCP connections across multiple +//! threads. +//! +//! To see this server in action, you can run this in one terminal: +//! +//! make A=apps/std/tokio-echo STD=y NET=y run +//! +//! and in another terminal you can run: +//! +//! cargo run --example connect 127.0.0.1:5555 +//! +//! Each line you type in to the `connect` terminal should be echo'd back to +//! you! If you open up multiple terminals running the `connect` example you +//! should be able to see them all make progress simultaneously. + +#![warn(rust_2018_idioms)] + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpListener; + +use std::env; +use std::error::Error; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), Box> { + // Allow passing an address to listen on as the first argument of this + // program, but otherwise we'll just set up our TCP listener on + // 127.0.0.1:5555 for connections. + let addr = env::args() + .nth(1) + .unwrap_or_else(|| "0.0.0.0:5555".to_string()); + + // Next up we create a TCP listener which will listen for incoming + // connections. This TCP listener is bound to the address we determined + // above and must be associated with an event loop. + let listener = TcpListener::bind(&addr).await?; + println!("Listening on: {}", addr); + + loop { + // Asynchronously wait for an inbound socket. + let (mut socket, _) = listener.accept().await?; + + // And this is where much of the magic of this server happens. We + // crucially want all clients to make progress concurrently, rather than + // blocking one on completion of another. To achieve this we use the + // `tokio::spawn` function to execute the work in the background. + // + // Essentially here we're executing a new task to run concurrently, + // which will allow all of our clients to be processed concurrently. + + tokio::spawn(async move { + let mut buf = vec![0; 1024]; + + // In a loop, read data from the socket and write the data back. + loop { + let n = socket + .read(&mut buf) + .await + .expect("failed to read data from socket"); + + if n == 0 { + return; + } + + socket + .write_all(&buf[0..n]) + .await + .expect("failed to write data to socket"); + } + }); + } +}