CodeBlanks Logo Icon CodeBlanks

Kanal ile mesaj

mpsc ile mesajları toplayıp dağıtırsın.

Kanal ile mesaj

Sohbette istemci thread'leri gelen mesajları merkezi bir yere iletmelidir. Her istemci işleyicisine bir Sender kopyası verilir; merkezi thread tek Receiver'dan okur. Böylece ortak durum kilitlenmeden mesajlar tek bir sırada toplanır:

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(())
}

İstemci bağlantıyı kapatınca lines() biter, thread sonlanır ve göndericisi bırakılır.

Kabul döngüsünü bağlamak

Kabul eden thread, her bağlantıya artan bir kimlik ve göndericinin bir kopyasını verir:

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 burada bırakılır
});

Gerçek bir sunucuda take olmaz; kapatma sinyali ayrı bir kanalla gönderilir.

Uygulama Merkeze ilet

Yukarıdaki istemci_isle ve kabul döngüsünü kur. Ana thread iki istemciyi sırayla bağlasın: biri selam, diğeri nasılsın yazıp bağlantıyı kapatsın. Ardından rx.iter() ile tüm mesajları topla, sırala ve her birini yazdır.

Başlangıç kodu

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
}

Örnek çözüm

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(())
}

Beklenen çıktı

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

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 Merkezi alıcı ne zaman "tüm mesajlar geldi" diyebilir?

  • Bir saniye sonra
  • Hiçbir zaman
  • Tüm göndericiler (her işleyicininki ve kabul döngüsününki) bırakıldığında Doğru cevap
  • İlk mesajda

Açıklama: Kabul döngüsü kendi tx kopyasını tutuyorsa, döngü bitince onu da bırakmalıdır.

2 Göndericiyi ver

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

Açıklama: Her istemci işleyicisi kendi gönderici kopyasını alır.

3 Bileşen ve görevi

Kavram Anlamı
Sender<String> İşleyiciden merkeze
Receiver Merkezi okuyucu
enumerate() İstemci kimliği
lines() biter İstemci ayrıldı

İleri seviye bölümündeki tüm dersler