Accepting sockets

The first thing our Redis server needs to do is to accept inbound TCP sockets. This is done with tokio::net::TcpListener.

Many of Tokio’s types are named the same as their synchronous equivalent in the Rust standard library. When it makes sense, Tokio exposes the same APIs as std but using async fn.

A TcpListener is bound to port 6379, then sockets are accepted in a loop. Each socket is processed then closed. For now, we will read the command, print it to stdout and respond with an error.

  1. use tokio::net::{TcpListener, TcpStream};
  2. use mini_redis::{Connection, Frame};
  3. #[tokio::main]
  4. async fn main() {
  5. // Bind the listener to the address
  6. let listener = TcpListener::bind("127.0.0.1:6379").await.unwrap();
  7. loop {
  8. // The second item contains the IP and port of the new connection.
  9. let (socket, _) = listener.accept().await.unwrap();
  10. process(socket).await;
  11. }
  12. }
  13. async fn process(socket: TcpStream) {
  14. // The `Connection` lets us read/write redis **frames** instead of
  15. // byte streams. The `Connection` type is defined by mini-redis.
  16. let mut connection = Connection::new(socket);
  17. if let Some(frame) = connection.read_frame().await.unwrap() {
  18. println!("GOT: {:?}", frame);
  19. // Respond with an error
  20. let response = Frame::Error("unimplemented".to_string());
  21. connection.write_frame(&response).await.unwrap();
  22. }
  23. }

Now, run this accept loop:

  1. $ cargo run

In a separate terminal window, run the hello-redis example (the SET/GET command from the previous section):

  1. $ cargo run --example hello-redis

The output should be:

  1. Error: "unimplemented"

In the server terminal, the output is:

  1. GOT: Array([Bulk(b"set"), Bulk(b"hello"), Bulk(b"world")])