Client thread
- Rust course
- Advanced
- Lesson 26/81
- 6 min
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
use std::io::{self, BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::thread;
fn main() {
// buraya yaz
}Sample solution
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
let mut yazici = akis.____()?;
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 |