Weekly Rust Trivia is a problem-oriented series of articles that assist developers while learning Rust. Every article solves simple, everyday development tasks using the Rust standard library or leveraging popular and proven crates.

Question: How to create a TCP server in Rust?

We can use the io and net and thread modules from Rust standard library this trivia.

use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle(mut stream: TcpStream) {
    // 64 KB buffer for demonstration purposes
    let mut buffer = [0; 65536];

    loop {
        let bytes_read = match stream.read(&mut buffer) {
            Ok(0) => return, // End of stream
            Ok(n) => n,
            Err(_) => return, // Error reading from stream
        };

        // Echo the data back to the client
        if let Err(_) = stream.write(&buffer[..bytes_read]) {
            return; // Error writing to stream
        }
    }
}

The handle function takes in a TcpStream object representing a client connection.

Inside the function, a buffer of size 65536 bytes (64 KB) is created to hold the incoming data. The function then enters an infinite loop and reads data from the client into the buffer. If the read operation returns an error, the function returns immediately.

If the read operation returns zero (0), it means that the end of the stream has been reached, and the function returns. If data is successfully read from the client, it is immediately written back to the client as an echo. If the write operation returns an error, the handle function will again return immediately.

Now that we walked through the handle function, we can use the TcpListener struct and expose our custom echo server on port 8080. For every incoming connection, we spawn a new thread using the spawn function of the thread module:

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080")?;

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                // Spawn a new thread to handle each incoming connection
                thread::spawn(move || handle(stream));
            }
            Err(e) => {
                println!("Error accepting connection: {}", e);
            }
        }
    }

    Ok(())
}

We can connect to our custom TCP-Echo server using telnet:

telnet localhost 8080
# Trying ::1...
# telnet: connect to address ::1: Connection refused
# Trying 127.0.0.1...
# Connected to localhost.
# Escape character is '^]'.
# Hello
# Hello
# Bye
# Bye
# telnet> Connection closed.