CodeBlanks Logo Icon CodeBlanks

Channel messaging

Collect and dispatch messages with mpsc.

Messages through a channel

In a chat, client threads must forward incoming messages to a central place. Each client handler is given a copy of a Sender; the central thread reads from the single Receiver. This way messages gather in a single order without locking shared state:

fn istemci_isle(akis: TcpStream, id: usize, tx: Sender<String>) -> io::Result<()> {
    for s in BufReader::new(akis).lines() {
        tx.send(format!("[{id}] {}", s?)).ok();
    }
    Ok(())
}

When the client closes the connection, lines() ends, the thread finishes and its sender is dropped.

Wiring up the accept loop

The accepting thread gives each connection an increasing id and a copy of the sender:

let (tx, rx) = mpsc::channel();
thread::spawn(move || {
    for (id, akis) in dinleyici.incoming().take(2).enumerate() {
        let tx = tx.clone();
        let akis = akis.unwrap();
        thread::spawn(move || istemci_isle(akis, id, tx));
    }
    // tx is dropped here
});

A real server has no take; a shutdown signal is sent through a separate channel.

Practice Forward to the center

Build istemci_isle and the accept loop above. Let the main thread connect two clients in turn: one writes selam, the other nasılsın, each closing its connection. Then collect all messages with rx.iter(), sort them and print each one.

Starter code

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

fn main() {
    // buraya yaz
}

Sample solution

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

fn istemci_isle(akis: TcpStream, id: usize, tx: Sender<String>) -> io::Result<()> {
    for s in BufReader::new(akis).lines() {
        tx.send(format!("[{id}] {}", s?)).ok();
    }
    Ok(())
}

fn main() -> io::Result<()> {
    let dinleyici = TcpListener::bind("127.0.0.1:0")?;
    let adres = dinleyici.local_addr()?;
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        for (id, akis) in dinleyici.incoming().take(2).enumerate() {
            let tx = tx.clone();
            let akis = akis.unwrap();
            thread::spawn(move || istemci_isle(akis, id, tx));
        }
    });
    for mesaj in ["selam", "nasılsın"] {
        let mut akis = TcpStream::connect(adres)?;
        writeln!(akis, "{mesaj}")?;
    }
    let mut hepsi: Vec<String> = rx.iter().collect();
    hepsi.sort();
    for m in hepsi {
        println!("{}", m);
    }
    Ok(())
}

Expected output

[0] selam
[1] nasılsın

Questions and answers

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

1 When can the central receiver say "all messages have arrived"?

  • After a second
  • Never
  • When all senders (each handler's and the accept loop's) are dropped Correct answer
  • At the first message

Explanation: If the accept loop keeps its own tx copy, it must drop it too when the loop ends.

2 Hand over the sender

Question
let tx = tx.____();
thread::spawn(move || istemci_isle(akis, id, tx));
Answer
let tx = tx.clone();
thread::spawn(move || istemci_isle(akis, id, tx));

Explanation: Each client handler gets its own sender copy.

3 Component and role

Term Meaning
Sender<String> Handler to center
Receiver Central reader
enumerate() Client id
lines() biter Client left

All lessons in Advanced