Rust
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".
Exercise 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.
Show example 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(())
}
Test yourself
-
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
Show answer
With tens of thousands of clients, per-thread memory and switching cost grow — At that scale async (tokio) is preferred; for few clients the thread model is simple and enough.
-
Second handle
let mut yazici = akis.____()?;
Show answer
let mut yazici = akis.try_clone()?;
-
Piece and role
- thread::spawn(..istemci_isle)
- Thread per connection
- try_clone()
- Read/write split
- lines()
- Line-by-line reading
- incoming().take(2)
- Stop after two connections
Next lesson: Channel messaging