Rust Day 6: Tokio — Simple TCP Client

Arjun Sunil Kumar
Go Rust
Published in
6 min readApr 26, 2022

--

Today, I am trying out tokio, an async runtime for rust. I think it is similar to Promise in js or Future in Java.

Also, I haven’t started using Cargo much. I think this project would pave a good path to understanding the capability of the dependency manager.

Cargo Commands:

Crate vs mod:

From Rust Docs: A crate is a compilation unit in Rust. Whenever rustc some_file.rs is called, some_file.rs is treated as the crate file. If some_file.rs has mod declarations in it, then the contents of the module files would be inserted in places where mod declarations in the crate file are found, before running the compiler over it. In other words, modules do not get compiled individually, only crates get compiled.

A crate can be compiled into a binary or into a library. By default, rustc will produce a binary from a crate. This behavior can be overridden by passing the --crate-type flag to lib.

Cargo: Lib vs Bin:

From SO: A binary crate should generate an executable (or multiple) that can be installed in the user’s path and can be executed as usual.

The purpose of a library crate on the other hand is not to create executables but rather to provide functionality for other crates to depend on and use.

Examples in Cargo:

From Rust Docs: Files located under the examples directory are example uses of the functionality provided by the library. When compiled, they are placed in the target/debug/examples directory.

By default, examples are executable binaries (with a main() function). You can specify the crate-type field to make an example be compiled as a library:

[[example]]
name = "foo"
crate-type = ["staticlib"]

You can run individual executable examples with the cargo run command with the --example <example-name> option.

RUST Pounds (#) aka Attributes:

https://doc.rust-lang.org/reference/attributes.html
https://doc.rust-lang.org/reference/attributes.html

Document vs Comment:

https://learning-rust.github.io/docs/a5.comments_and_documenting_the_code.html

Lints:

https://doc.rust-lang.org/rustc/lints/index.html
https://doc.rust-lang.org/rustc/lints/levels.html
https://doc.rust-lang.org/rustc/lints/groups.html

Idiom Lints:

https://hackmd.io/@nikomatsakis/SJggBfQbd
  • To declare rust_2018_idioms as a warning, you can use try:
  • To deny this idiom, use
Here you can see a red underline on the problem area
  • To allow, this idiom, use
No yellow highlight nor red underline

Traits (Interfaces in Java)

https://doc.rust-lang.org/book/ch10-02-traits.html
  • Here Tweet class implements Summary Interface.
  • We see how to invoke summarize() function

Dyn Keyword:

In java, it would be something like

MemoryHeap<Debug> x; // ignore MemoryHeap for now

Box:

https://doc.rust-lang.org/rust-by-example/std/box.html

From FasterThanLI

Without Box:

With Box:

Result:

https://doc.rust-lang.org/std/result/

Error:

A wrapper class similar to Exception in java.

Async Await:

https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html

Here, Tokio is our executor.

https://blog.logrocket.com/a-practical-guide-to-async-in-rust/

Writing async code:

https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html
await

Await: We won’t run the code inside async (Future) until await is called. Can be thought of w.r.t Big Data Frameworks like Spark or Flink, where they don’t do execution of DAG, until the execute or print is invoked in the Driver.

? in RUST:

https://doc.rust-lang.org/book/appendix-02-operators.html

It is used to propagate error when it occurs. Think of it as more of like making a Runtime exception (unchecked) in java, as a checked Exception.

  • I got a connection refused error with ?
  • But without ?, I went to the Stream Writer on the connection, which resulted in a fatal error

Netcat:

Netcat is a networking utility that reads and writes data across network connections, using the TCP/IP protocol.

https://www.oreilly.com/library/view/practical-web-penetration/9781788624039/a6bdd6aa-c564-4172-9c31-c15ae1a09bd4.xhtml

We will be using it as a Server

nc -l 6142

RUST CODE:

Async Hello World Client: This is a program taken from tokio_examples.

  • Cargo.toml
[package]
name = "tokio_sample"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.17.0", features = ["full"] }

[[example]]
name = "hello_world"
path = "src/examples/hello_world.rs"

Here you can see,

  1. tokio dependency added to the manifest.
  2. adding an example (hello_world) section in the manifest.
  • hello_world.rs

All the topics covered above can be seen in this simple hello_world code.

  1. //! is used for documentation.
  2. #![warn(rust_2018_idioms)] Warn the user, when he uses 2018 rust syntax.
  3. #[tokio::main]: the main function is not allowed to be async. We need to use the attribute to help the compiler understand that, it is supported by tokio.
  4. async : To return Future and write async code.
  5. .await : Suspend execution until the result of a Future is ready.
  6. ?: Use to throw the runtime exception.
  7. let mut stream = TcpStream::connect(“127.0.0.1:6142”).await?;

Here we are opening a new TCP Stream. We await until that stream is opened. If there is an error in connecting, then we throw that error using ?.

TcpStream::connect(“ip4:port#”) is provided by tokio_net package.

NOTE: In mac, we are running

nc -l 6142

to open a listening server. Our code above is more like a client connecting to that server.

8. let result = stream.write(b”hello world\n”).await;

Here we are writing bytes to the stream. We are able to write, by importing

use tokio::io::AsyncWriteExt;

Here we are not throwing the error to the main. Probably because we anyways reached almost the end.

9. {:?} is used to debug print.

10. result.is_ok() will return a boolean, if written or not.

11. Ok(()) is used to signify a Positive response, with an empty body.

Conclusion:

This was a lengthy one, as I was kind of introducing every simple concept of async rust programming. In the next article, we will see how we can replace Netcat, with our own server, Rc vs Arc, and things like that. Until then, have a great day!

Found it Interesting?

Please show your support by 👏.

--

--

Arjun Sunil Kumar
Go Rust

Writes on Database Kernel, Distributed Systems, Cloud Technology, Data Engineering & SDE Paradigm. github.com/arjunsk