Jan 15, 2024

Build a low latency TCP server using RUST

TCP Server

Steps

  • Setup your Rust environment
  • Add tokio, serde, serde_json to your Cargo.toml
  • Define commands
  • Build Server
  • Test Server

I. Setup your RUST environment

cargo new hash_example
cd hash_example

II. Add crates: sha3 as a dependency

[dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

III. commands.rs

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub enum Command {
GetHelloWorld,
PostGreeting { message: String },
}

IV. tcp_server.rs

use crate::commands::Command;
use std::io;
use std::net::SocketAddr;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
use tokio::net::{TcpListener, TcpStream};

pub async fn start_server(addr: &str) -> io::Result<()> {
let addr = addr.parse::<SocketAddr>().unwrap();
let listener = TcpListener::bind(addr).await?;

loop {
let (socket, _) = listener.accept().await?;
tokio::spawn(async move {
handle_client(socket).await.unwrap();
});
}
}

async fn handle_client(socket: TcpStream) -> io::Result<()> {
let (reader, writer) = socket.into_split();
let mut reader = BufReader::new(reader);
let mut writer = BufWriter::new(writer);

loop {
let mut buffer = String::new();
reader.read_line(&mut buffer).await?;

if buffer.is_empty() {
break;
}

let command: Command = match serde_json::from_str(&buffer) {
Ok(cmd) => cmd,
Err(_) => break,
};

let response = match command {
Command::GetHelloWorld => "Hello, world!".to_string(),
Command::PostGreeting { message } => format!("Received greeting: {}", message),
};

writer.write_all(response.as_bytes()).await?;
writer.flush().await?;
}

Ok(())
}

V. main.rs

mod commands;
mod tcp_server;

#[tokio::main]
async fn main() -> std::io::Result<()> {
tcp_server::start_server("127.0.0.1:8080").await
}

Run issuing below command

cargo run

VI. unit tests bin/client.rs

use serde_json::json;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::net::TcpStream;

fn main() -> io::Result<()> {
let stream = TcpStream::connect("127.0.0.1:8080")?;
let mut writer = BufWriter::new(&stream);
let mut reader = BufReader::new(&stream);

// Send GetHelloWorld command
let get_hello = json!({"GetHelloWorld": {}}).to_string() + "\n";
writer.write_all(get_hello.as_bytes())?;
writer.flush()?;

// Read the response
let mut response = String::new();
reader.read_line(&mut response)?;
println!("Response: {}", response.trim());

// Send PostGreeting command
let post_greeting = json!({"PostGreeting": {"message": "Hello, Rust!"}}).to_string() + "\n";
writer.write_all(post_greeting.as_bytes())?;
writer.flush()?;

// Read the response
response.clear();
reader.read_line(&mut response)?;
println!("Response: {}", response.trim());

Ok(())
}

Run issuing below command

cargo run --bin client

You can find complete code at:

https://github.com/Nepalichhoro/Low_latency_tcp_server_rust


Thanks for reading! If you want to see future content, you can follow me on Twitter or get connected over at LinkedIn.


Support My Content

If you find my content helpful, consider supporting a humanitarian cause (building homes for elderly people in rural Terai region of Nepal) that I am planning with your donation:

Ethereum (ETH)

0xb0b1B20062DA9Dd7BaA4D5b088DF49dbe4b46fF2

Thank you for your support!