diff --git a/config.yaml b/config.yaml index ab1a1fec..4e7880cd 100644 --- a/config.yaml +++ b/config.yaml @@ -14,6 +14,9 @@ menu: name: "API documentation" url: "https://docs.rs/tokio" weight: 9000 + - identifier: "network_utils" + name: "Network Utilities" + weight: 10000 params: diff --git a/content/docs/getting-started/cargo-dependencies.md b/content/docs/getting-started/cargo-dependencies.md new file mode 100644 index 00000000..4544e842 --- /dev/null +++ b/content/docs/getting-started/cargo-dependencies.md @@ -0,0 +1,136 @@ +--- +title: "Cargo dependencies" +weight : 1020 +menu: + docs: + parent: getting_started +--- + +Feel free to skip this section, and just use `features = ["full"]` while +learning. This section provides some examples of how and when to choose +specific [`feature groups`] in `Cargo.toml`. + +## Often using all features is fine + +When writing an app, we'll often use all of Tokio's features to accelerate +our development time. + +Cargo.toml: +```toml +[dependencies] +tokio = { version = "0.2", features = ["full"] } +``` + +For most apps, the additional compile time is hardly noticeable relative to the +cognitive overhead of thinking about which features are needed. + +# Selecting features + +When we develop protocol implementations that are used by client apps and +servers, we want to limit the dependencies to decrease compile time -- +not just for us, but also for the users of our crates. + +Let's take a look at our tiny client app and see what features are needed. + +## tokio main + +If we replace our `src/main.rs` with following code (only adding the +`#[tokio:main]` macro to the default hello world generated by `cargo new`): + +main.rs: +```rust +#[tokio::main] +async fn main() { + println!("doing nothing yet"); +} +``` + +Then we can choose whether to use threads (commonly used for servers): + +```toml +tokio = { version = "0.2", features = ["macros", "rt-threaded"] } +``` + +or lightweight cooperative multi-tasking +(often required for low-profile clients): + +```toml +tokio = { version = "0.2", features = ["macros", "rt-core"] } +``` + +## TcpStream connect + +As we start to build the app, we'll need more features. The line of code +where we call `TcpStream::connect` requires two features: + + +```rust,no_run +# #![deny(deprecated)] +# #![allow(unused_mut)] +# #![allow(unused_variables)] +# use tokio::net::TcpStream; +# #[tokio::main] +# async fn main() { + let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap(); +# } +``` + + +1. `dns` for converting the string `127.0.0.1:6142` to an IP address -- Tokio +will need to do a DNS lookup just in case. +2. `tcp` for handling the TCP connection + + +Since our example uses a specific ip address, we can reduce dependendencies +further by creating a [`std::net::SocketAddr`]: + +```rust,no_run +# #![deny(deprecated)] +# #![allow(unused_mut)] +# #![allow(unused_variables)] +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use tokio::net::TcpStream; + +#[tokio::main] +async fn main() { + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6142); + let mut stream = TcpStream::connect(addr).await; +} +``` + +## Writing to the socket + +The `write` method is defined in [`tokio::io::AsyncWriteExt`] which requires +`io-util`. + +```rust,no_run +# #![deny(deprecated)] +# #![allow(unused_variables)] +# use tokio::net::TcpStream; +# use tokio::prelude::*; +# #[tokio::main] +# async fn main() { +# let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap(); + let result = stream.write(b"hello world\n").await; +# } + +``` + +The `write` method in our example is declared with `use tokio::prelude::*`. +The [`tokio prelude`] follows the [`Rust prelude pattern`] to make using +multiple types more convenient. We could be more explicit by declaring `use tokio::io::AsyncWriteExt`: + + +## Learn more about features + +The [`API reference`] indicates which features are need for different APIs. +If anything missing, please [`open an issue`] and one of the Tokio elves +will figure out where it is declared and will fix it asap! + +[`feature groups`]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section +[`std::net::SocketAddr`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html +[`tokio::io::AsyncWriteExt`]: https://docs.rs/tokio/*/tokio/io/trait.AsyncWriteExt.html +[`tokio prelude`]: https://docs.rs/tokio/0.2.9/tokio/prelude/index.html +[`Rust prelude pattern`]: https://doc.rust-lang.org/std/prelude/index.html#other-preludes +[`API reference`]: https://docs.rs/tokio/ +[`open an issue`]: https://github.com/tokio-rs/tokio/issues/new diff --git a/content/docs/getting-started/hello-world.md b/content/docs/getting-started/hello-world.md index dc521bba..2d504b23 100644 --- a/content/docs/getting-started/hello-world.md +++ b/content/docs/getting-started/hello-world.md @@ -1,150 +1,145 @@ --- -title: "Hello World!" +title: "Hello world!" weight : 1010 menu: docs: parent: getting_started --- -To kick off our tour of Tokio, we will start with the obligatory "hello world" -example. This program will create a TCP stream and write "hello, world!" to the stream. -The difference between this and a Rust program that writes to a TCP stream without Tokio -is that this program won't block program execution when the stream is created or when -our "hello, world!" message is written to the stream. - -Before we begin you should have a very basic understanding of how TCP streams -work. Having an understanding of Rust’s [standard library -implementation](https://doc.rust-lang.org/std/net/struct.TcpStream.html) is also -helpful. - -Let's get started. - -First, generate a new crate. +This guide assumes that a knowledge of the [Rust programming language] and are +using Rust `1.39.0` or higher. To use Tokio with earlier versions of Rust, +please check out the [Tokio 0.1 docs]. +To check version of Rust on the command line: ```bash -$ cargo new hello-world -$ cd hello-world +rustc --version +rustc 1.39.0 (4560ea788 2019-11-04) ``` -Next, add the necessary dependencies in `Cargo.toml`: +In version `1.39.0`, [Rust introduced async-await], see the [Async Book] for +in-depth documentation of these new Rust language features. This guide also +seeks to provide a practical, hands-on introduction for all of the Rust language +features that are needed for programming with Tokio. -```toml -[dependencies] -tokio = { version = "0.2", features = ["full"] } -``` +Also, most readers will have some experience with writing networking code +using the [Rust standard library] or another language, though folks who are +new to network programming should be able to follow along. -Tokio requires specifying the requested components using feature flags. This -allows the user to only include what is needed to run the application, resulting -in smaller binaries. For getting started, we depend on `full`, which includes -all components. +# Introducing asynchronous programming -Next, add the following to `main.rs`: +As our first introduction to [asynchronous programming](../overview) with +Tokio, we will start with a tiny app that sends "hello, world!" over a +network connection. -```rust -# #![deny(deprecated)] -# #![allow(unused_imports)] +The difference between Tokio and a Rust program written using the standard +library is that Tokio has the flexibility to avoid blocking program execution +when the stream is created or when our “hello, world!” message is written +to the stream. -use tokio::io; -use tokio::net::TcpStream; -use tokio::prelude::*; +Tokio's [`net`] module provides the same abstraction over networking as +the corresponding modules in `std` except asynchronously. -#[tokio::main] -async fn main() { - // application comes here -} -``` +# Communicating through a network connection -Here we use Tokio's own [`io`] and [`net`] modules. These modules provide the same -abstractions over networking and I/O-operations as the corresponding modules in -`std` with a difference: all actions are performed asynchronously. +When developing network code, it's helpful to use tools that let us simulate +each end of the connection. We've chosen two to allow us to receive text +(*listen* on a socket and *read* data) and send text (*write* data). -Next is the Tokio application entry point. This is an `async` main function -annotated with `#[tokio::main]`. This is the function that first runs when the -binary is executed. The `#[tokio::main]` annotation informs Tokio that this is -where the runtime (all the infrastructure needed to power Tokio) is started. +We'll start by sending "hello" over a reliable networking connection ([`TCP`]). +Before writing code in Rust, let's install and use some network tools to +manually do what our code will do later in the guide. -# Creating the TCP stream +Install [`socat`](../network-utilities/socat), which is a network utility that +we'll use to simulate a server. Then type the following command to print +out everything that is received on port 6142 (a somewhat arbitrary number +we have chosen for this example): -The first step is to create the `TcpStream`. We use the `TcpStream` implementation -provided by Tokio. +```bash +socat TCP-LISTEN:6142, fork stdout +``` -```rust,no_run -# #![deny(deprecated)] -# -# use tokio::net::TcpStream; -#[tokio::main] -async fn main() { - // Connect to port 6142 on localhost - let stream = TcpStream::connect("127.0.0.1:6142").await.unwrap(); +An easy way to simulate a client (for a text-based protocol) is to use +[`telnet`]. To connect to our simulated server, we'll open a different +window so we can see two terminals side-by-side and type the following on the +command-line: - // Following snippets come here... -# drop(stream); -} +```bash +telnet localhost 6142 +``` + +Telnet will output something like this: +```bash +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. ``` -`TcpStream::connect` is an _asynchronous_ function. No work is done during the -function call. Instead, `.await` is called to pause the current task until the -connect has completed. Once the connect has completed, the task resumes. The -`.await` call does **not** block the current thread. +Now if we type `hello` and press return, we should see `hello` printed by +socat. -Next, we do work with the TCP stream. +To *escape* from our TCP session (`^]`), we need to hold down `Ctrl` key and +type `]`). Then at the telnet prompt (`telnet >`), typing `quit` will close +the connection and exit the program. -# Writing data +Let's quit telnet and write some Rust code to send some text to our server. +We'll leave socat running, so we can connect to it with our new app! -Our goal is to write `"hello world\n"` to the stream. +# Let's write some code! -```rust,no_run -# #![deny(deprecated)] -# -# use tokio::net::TcpStream; -# use tokio::prelude::*; -# #[tokio::main] -# async fn main() { -// Connect to port 6142 on localhost -let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap(); - -stream.write_all(b"hello world\n").await.unwrap(); - -println!("wrote to stream"); -# } +Next we'll write some code to create a TCP stream and write “hello, world!” +to the stream using Tokio. + +Let's get started by generating a new Rust app: + +```bash +$ cargo new hello-world +$ cd hello-world ``` -The [`write_all`] function is implemented for all "stream" like types. It is -provided by the [`AsyncWriteExt`] trait. Again, the function is asynchronous, so -no work is done unless `.await` is called. We call `.await` to perform the -write. +For a quick start, we'll add the Tokio crate with all Tokio of its features +to our Cargo manifest, by adding the following text to `Cargo.toml`. -You can find the full example [here][full-code]. +```toml +[dependencies] +tokio = { version = "0.2", features = ["full"] } +``` -# Running the code +Then we'll replace `main.rs` with our "hello world" code: -[Netcat] is a tool for quickly creating TCP sockets from the command line. The following -command starts a listening TCP socket on the previously specified port. +```rust,no_run +# #![deny(deprecated)] -```bash -$ nc -l 6142 -``` -> The command above is used with the GNU version of netcat that comes stock on many -> unix based operating systems. The following command can be used with the -> [NMap.org][NMap.org] version: `$ ncat -l 6142` +use tokio::net::TcpStream; +use tokio::prelude::*; -In a different terminal we'll run our project. +#[tokio::main] +async fn main() { + let mut stream = TcpStream::connect("127.0.0.1:6142").await.unwrap(); + println!("created stream"); -```bash -$ cargo run + let result = stream.write(b"hello world\n").await; + println!("wrote to stream; success={:?}", result.is_ok()); +} ``` -If everything goes well, you should see `hello world` printed from Netcat. +The `#[tokio::main]` macro provides common boilerplate for setting up the +tokio runtime, so that we can write `main()` as an [`async function`]. This +enables us to call asynchronous functions and write sequential code as if +they were blocking by using the Rust `await` keyword. -# Next steps +Now when we run our app with `cargo run` (in a different window from where +we are running socat), we should see socat print `hello world`. -We've only dipped our toes into Tokio and its asynchronous model. The next page in -the guide will start digging a bit deeper into Futures and the Tokio runtime model. +# Next steps -[`io`]: https://docs.rs/tokio/0.2/tokio/io/index.html -[`net`]: https://docs.rs/tokio/0.2/tokio/net/index.html -[`write_all`]: https://docs.rs/tokio/0.2/tokio/io/trait.AsyncWriteExt.html#method.write_all -[`AsyncWriteExt`]: https://docs.rs/tokio/0.2/tokio/io/trait.AsyncWriteExt.html -[full-code]: https://github.com/tokio-rs/tokio/blob/master/examples/hello_world.rs -[Netcat]: http://netcat.sourceforge.net/ -[Nmap.org]: https://nmap.org +Now that we have successfully built a tiny client to get a feel for network +programming with Tokio, we'll dive into more detail about how everything works. + +[Rust programming language]: https://www.rust-lang.org/ +[Tokio 0.1 docs]: https://v0-1--tokio.netlify.com/docs/getting-started/hello-world/ +[Rust introduced async-await]: https://blog.rust-lang.org/2019/11/07/Async-await-stable.html +[Async Book]: https://rust-lang.github.io/async-book/index.html +[Rust standard library]: https://doc.rust-lang.org/std/net/index.html +[`TCP`]: (https://tools.ietf.org/html/rfc793 +[`net`]: https://docs.rs/tokio/*/tokio/net/index.html +[`async function`]: https://doc.rust-lang.org/reference/items/functions.html#async-functions diff --git a/content/docs/network-utilities/socat.md b/content/docs/network-utilities/socat.md new file mode 100644 index 00000000..205fca14 --- /dev/null +++ b/content/docs/network-utilities/socat.md @@ -0,0 +1,36 @@ +--- +title: "socat" +menu: + docs: + parent: network_utils +--- + +[socat](http://www.dest-unreach.org/socat/) provides the ability to redirect +input and outputs. See instructions and links below for quick references on +installation for most operating systems. + +# How to install + +## Debian / Ubuntu Linux + +```bash +sudo apt-get update && sudo apt-get install socat +``` + +## Fedora, CentOS, RedHat + +```bash +sudo yum install socat +``` + +## Mac Homebrew + +```bash +brew install socat +``` + +## Windows + +[download](https://sourceforge.net/projects/unix-utils/files/socat/) + + diff --git a/content/docs/network-utilities/telnet.md b/content/docs/network-utilities/telnet.md new file mode 100644 index 00000000..b44b33ae --- /dev/null +++ b/content/docs/network-utilities/telnet.md @@ -0,0 +1,41 @@ +--- +title: "telnet" +menu: + docs: + parent: network_utils +--- +The word "telnet" often refers to the Telnet client, since the command to +run the client has the same name as the protocol. This page is about the +telnet client which we use in this guide to test and debug servers. + +Telnet is a computer protocol that was built for interacting with remote +computers ([IETF RFC-854]: https://tools.ietf.org/html/rfc854). + +Telnet utility allows users to test connectivity to remote machines and +issue commands through the use of a keyboard, and one of the simplest ways to +check if a port is open and listening on a network. + +# How to install + +## Debian / Ubuntu Linux + +```bash +sudo apt-get update && sudo apt-get install telnet + +``` + +## Fedora, CentOS, RedHat + +```bash +sudo yum install telnet +``` + +## Mac Homebrew + +```bash +brew install telnet +``` + +## Windows + +[Install for CMD or PowerShell](https://www.shellhacks.com/windows-install-telnet-client-cmd-powershell/) \ No newline at end of file