CodeBlanks Logo Icon CodeBlanks

Client thread

Spawn and manage a thread per client.

Client threads

Handling clients one by one in a single loop means one slow client keeps everyone waiting. Giving each connection its own thread lets clients progress independently:

for akis in dinleyici.incoming() {
    let akis = akis?;
    thread::spawn(move || istemci_isle(akis));
}

fn istemci_isle(akis: TcpStream) -> io::Result<()> {
    let mut yazici = akis.try_clone()?;
    for satir in BufReader::new(akis).lines() {
        writeln!(yazici, "{}", satir?.to_uppercase())?;
    }
    Ok(())
}

try_clone gives a second handle to the same socket: one for reading, one for writing.

An echo protocol

To test the protocol, let the server turn each line to uppercase and send it back. The client side writes one line and reads one line:

fn sor(adres: SocketAddr, mesaj: &str) -> io::Result<String> {
    let mut akis = TcpStream::connect(adres)?;
    writeln!(akis, "{mesaj}")?;
    let mut yanit = String::new();
    BufReader::new(akis).read_line(&mut yanit)?;
    Ok(yanit.trim().to_string())
}

to_uppercase applies Unicode rules: "dünya" → "DÜNYA".

Practice Echo server

Run the server on a thread: let it accept two connections with incoming().take(2) and handle each on a separate thread with istemci_isle. Let the main thread send merhaba, then dünya with the sor function and print the replies on separate lines.

Starter code

main.rs
use std::io::{self, BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::thread;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::io::{self, BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::thread;

fn istemci_isle(akis: TcpStream) -> io::Result<()> {
    let mut yazici = akis.try_clone()?;
    for s in BufReader::new(akis).lines() {
        writeln!(yazici, "{}", s?.to_uppercase())?;
    }
    Ok(())
}

fn sor(adres: SocketAddr, mesaj: &str) -> io::Result<String> {
    let mut akis = TcpStream::connect(adres)?;
    writeln!(akis, "{mesaj}")?;
    let mut yanit = String::new();
    BufReader::new(akis).read_line(&mut yanit)?;
    Ok(yanit.trim().to_string())
}

fn main() -> io::Result<()> {
    let dinleyici = TcpListener::bind("127.0.0.1:0")?;
    let adres = dinleyici.local_addr()?;
    thread::spawn(move || {
        for akis in dinleyici.incoming().take(2) {
            let akis = akis.unwrap();
            thread::spawn(move || istemci_isle(akis));
        }
    });
    println!("{}", sor(adres, "merhaba")?);
    println!("{}", sor(adres, "dünya")?);
    Ok(())
}

Expected output

MERHABA
DÜNYA

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the limit of the thread-per-client model?

  • There can't be more than two clients
  • Messages get mixed up
  • There's no limit
  • With tens of thousands of clients, per-thread memory and switching cost grow Correct answer

Explanation: At that scale async (tokio) is preferred; for few clients the thread model is simple and enough.

2 Second handle

Question
let mut yazici = akis.____()?;
Answer
let mut yazici = akis.try_clone()?;

Explanation: try_clone gives a second TcpStream for the same socket.

3 Piece and role

Term Meaning
thread::spawn(..istemci_isle) Thread per connection
try_clone() Read/write split
lines() Line-by-line reading
incoming().take(2) Stop after two connections

All lessons in Advanced